mirror of
https://github.com/HaitiData/haitidata.git
synced 2026-11-09 08:17:14 +00:00
Merge pull request #7 from euclidian/develop
Move Haiti Data to Kartoza's standard project structure
This commit is contained in:
commit
e5fd5ddcdf
596 files changed
+26170
-8843
No files matched your search
+45
-12
@@ -1,13 +1,10 @@
|
||||
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
@@ -25,17 +22,14 @@ var/
|
||||
*.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/
|
||||
@@ -45,23 +39,62 @@ htmlcov/
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Downstream Project
|
||||
downloaded/
|
||||
geoserver/
|
||||
uploaded/
|
||||
development.db
|
||||
development.db-journal
|
||||
# PyCharm
|
||||
.idea
|
||||
venv
|
||||
*.pyc
|
||||
*.*~
|
||||
vagrant-fabric-lxc
|
||||
django_project/media/images
|
||||
visual_changelog.db
|
||||
.vagrant
|
||||
node_modules
|
||||
id_dsa.pub
|
||||
id_rsa.pub
|
||||
docs/build
|
||||
fabgis_resources
|
||||
private.py
|
||||
# Vim leftovers
|
||||
# project specifc
|
||||
django_project/core/settings/secret.py
|
||||
xmlrunner
|
||||
# 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.
|
||||
#PyCharm
|
||||
# Virtual env
|
||||
# Node
|
||||
django_project/static
|
||||
# Working data not intended to be versioned
|
||||
data_for_import
|
||||
scrap.html
|
||||
.pycharm_helpers
|
||||
.sudo_as_admin_successful
|
||||
# Pg cluster for docker
|
||||
deployment/pg
|
||||
# Template for apt-cacher
|
||||
# static and media dirs
|
||||
deployment/static
|
||||
deployment/media
|
||||
deployment/docker/71-apt-cacher-ng
|
||||
django_project/geonode
|
||||
django_project/celerybeat-schedule
|
||||
# local media
|
||||
django_project/media
|
||||
# OSX Crap
|
||||
deployment/backups
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,30 @@
|
||||
# Haitidata
|
||||
|
||||
You should write some docs, it's good for the soul.
|
||||
|
||||
# Docker installation
|
||||
|
||||
## Install docker
|
||||
This will install an Haitidata container
|
||||
|
||||
Makes sure to have docker-compose installed
|
||||
```
|
||||
sudo apt-get install python-pip
|
||||
sudo pip install docker-compose
|
||||
```
|
||||
|
||||
## Get project code
|
||||
Clone the repo:
|
||||
```
|
||||
git clone https://github.com/HaitiData/haitidata.git
|
||||
```
|
||||
|
||||
## Run project
|
||||
Run the docker container:
|
||||
```
|
||||
cd deployment
|
||||
make deploy
|
||||
make update-migrations-localization:
|
||||
```
|
||||
|
||||
then you can visit http://0.0.0.0:33300```
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
Haitidata
|
||||
========================
|
||||
|
||||
You should write some docs, it's good for the soul.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install geonode with::
|
||||
|
||||
$ sudo add-apt-repository ppa:geonode/stable
|
||||
|
||||
$ sudo apt-get update
|
||||
|
||||
$ sudo apt-get install geonode
|
||||
|
||||
Create a new template based on the geonode example project.::
|
||||
|
||||
$ django-admin startproject my_geonode --template=https://github.com/GeoNode/geonode-project/archive/2.6.zip -epy,rst
|
||||
$ sudo pip install -e my_geonode
|
||||
|
||||
.. note:: You should NOT use the name geonode for your project as it will conflict with the default geonode package name.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Rename the local_settings.py.sample to local_settings.py and edit it's content by setting the SITEURL and SITENAME.
|
||||
|
||||
Edit the file /etc/apache2/sites-available/geonode and change the following directive from:
|
||||
|
||||
WSGIScriptAlias / /var/www/geonode/wsgi/geonode.wsgi
|
||||
|
||||
to:
|
||||
|
||||
WSGIScriptAlias / /path/to/my_geonode/my_geonode/wsgi.py
|
||||
|
||||
To avoid having to grant apache permissions (i.e. www-data user and group) to your home dir where you likely setup the geonode-project; you may want to instead copy the wsgi.py file next to geonode.wsgi and replace the file name instead of the entire path.
|
||||
|
||||
$ cp /path/to/my_geonode/my_geonode/wsgi.py /var/www/geonode/wsgi/wsgi.py
|
||||
|
||||
Add the "Directory" directive for your folder like the following example:
|
||||
|
||||
<Directory "/home/vagrant/my_geonode/my_geonode/">
|
||||
|
||||
Order allow,deny
|
||||
|
||||
Options Indexes FollowSymLinks
|
||||
|
||||
Allow from all
|
||||
|
||||
Require all granted
|
||||
|
||||
IndexOptions FancyIndexing
|
||||
|
||||
</Directory>
|
||||
|
||||
Restart apache::
|
||||
|
||||
$ sudo service apache2 restart
|
||||
|
||||
Edit the templates in my_geonode/templates, the css and images to match your needs.
|
||||
|
||||
In the my_geonode folder run::
|
||||
|
||||
$ python manage.py collectstatic
|
||||
|
||||
Github Considerations
|
||||
------------------------
|
||||
|
||||
While it is helpful to recommit your django project wrapper back to a distributed version control repository.
|
||||
* It is also important to remember that production instances will store security information in the local_settings.py
|
||||
* Admin/Devs should always remember to exclude this file in the .gitignore file in the same folder as the .git::
|
||||
|
||||
$ nano .gitignore
|
||||
|
||||
/{project}/local_settings.py
|
||||
|
||||
save, make sure the file is also removed from git cache::
|
||||
|
||||
$ git rm -f --cache //local_settings.py
|
||||
|
||||
$ git status
|
||||
|
||||
confirm the file is no longer staged for the next commit or that if it is as "removed"
|
||||
@@ -0,0 +1,386 @@
|
||||
PROJECT_ID := haitidata
|
||||
PRODUCTION_HOST := haitidata.com
|
||||
|
||||
SHELL := /bin/bash
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# P R O D U C T I O N C O M M A N D S
|
||||
# ----------------------------------------------------------------------------
|
||||
fresh-web: build permissions web
|
||||
|
||||
# Use this for fresh build (it will restore with empty stable database)
|
||||
fresh-deploy: fresh-web
|
||||
@docker exec -t -i $(PROJECT_ID)-db su - postgres -c "dropdb gis"
|
||||
@docker exec -t -i $(PROJECT_ID)-db su - postgres -c "createdb -O docker -T template_postgis gis"
|
||||
@docker exec -t -i $(PROJECT_ID)-db pg_restore /fresh-backups/fresh-database.dmp | docker exec -i $(PROJECT_ID)-db su - postgres -c "psql gis"
|
||||
|
||||
|
||||
default: web
|
||||
run: build permissions web migrate collectstatic
|
||||
|
||||
deploy: run
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Bringing up fresh instance "
|
||||
@echo "You can access it on http://localhost:63300"
|
||||
@echo "------------------------------------------------------------------"
|
||||
|
||||
build:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Building in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) build uwsgi
|
||||
@docker-compose -p $(PROJECT_ID) build uwsgi-ephemeral
|
||||
|
||||
|
||||
web:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) up -d web
|
||||
@docker-compose -p $(PROJECT_ID) up -d uwsgi-ephemeral
|
||||
@# Dont confuse this with the dbbackup make command below
|
||||
@# This one runs the postgis-backup cron container
|
||||
@# We add --no-recreate so that it does not destroy & recreate the db container
|
||||
@docker-compose -p $(PROJECT_ID) up --no-recreate --no-deps -d dbbackups
|
||||
|
||||
permissions:
|
||||
# Probably we want something more granular here....
|
||||
# Your sudo password will be needed to set the file permissions
|
||||
# on logs, media, static and pg dirs
|
||||
@if [ ! -d "logs" ]; then mkdir logs; fi
|
||||
@if [ ! -d "media" ]; then mkdir media; fi
|
||||
@if [ ! -d "static" ]; then mkdir static; fi
|
||||
@if [ ! -d "backups" ]; then mkdir backups; fi
|
||||
@if [ -d "logs" ]; then sudo chmod -R a+rwx logs; fi
|
||||
@if [ -d "media" ]; then sudo chmod -R a+rwx media; fi
|
||||
@if [ -d "static" ]; then sudo chmod -R a+rwx static; fi
|
||||
@if [ -d "backups" ]; then sudo chmod -R a+rwx backups; fi
|
||||
|
||||
db:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running db in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) up -d db
|
||||
|
||||
nginx:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running nginx in production mode"
|
||||
@echo "Normally you should use this only for testing"
|
||||
@echo "In a production environment you will typically use nginx running"
|
||||
@echo "on the host rather if you have a multi-site host."
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) up -d nginx
|
||||
@echo "Site should now be available at http://localhost"
|
||||
|
||||
migrate:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running migrate static in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker exec -t -i $(PROJECT_ID)_uwsgi-ephemeral_1 /bin/bash -c "\
|
||||
python manage.py migrate"
|
||||
|
||||
update-migrations:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running update migrations in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) run uwsgi-ephemeral python manage.py makemigrations
|
||||
|
||||
|
||||
update-migrations-localization:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running update migrations in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) run uwsgi bash -c "python manage.py makemigrations && python manage.py migrate"
|
||||
|
||||
collectstatic:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Collecting static in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
#We need to run collect static in the same context as the running
|
||||
# uwsgi-ephemeral container it seems so I use docker exec here
|
||||
# no -it flag so we can run over remote shell
|
||||
@docker-compose -p $(PROJECT_ID) run uwsgi-ephemeral python manage.py collectstatic --noinput
|
||||
|
||||
reload:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Reload django project in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
# no -it flag so we can run over remote shell
|
||||
@docker exec $(PROJECT_ID)-uwsgi uwsgi --reload /tmp/django.pid
|
||||
|
||||
kill:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Killing in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) kill
|
||||
|
||||
kill-uwsgi:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Killing in uwsgi production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) kill uwsgi
|
||||
|
||||
kill-uwsgi-ephemeral:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Killing in uwsgi production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) kill uwsgi-ephemeral
|
||||
|
||||
rm: dbbackup rm-only
|
||||
|
||||
|
||||
rm-only: kill
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Removing production instance!!! "
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) rm
|
||||
|
||||
logs:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Showing uwsgi logs in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) logs uwsgi
|
||||
|
||||
dblogs:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Showing db logs in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) logs db
|
||||
|
||||
nginxlogs:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Showing nginx logs in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) logs web
|
||||
|
||||
shell:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Shelling in in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) run uwsgi-ephemeral /bin/bash
|
||||
|
||||
superuser:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Creating a superuser in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) run uwsgi-ephemeral python manage.py createsuperuser
|
||||
|
||||
reviewaccess:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Creating review permissions in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) run uwsgi-ephemeral python manage.py create_review_permissions
|
||||
|
||||
dbbash:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Bashing in to production database"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker exec -t -i $(PROJECT_ID)-db /bin/bash
|
||||
|
||||
dbsnapshot:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Grab a quick snapshot of the database and place in the host filesystem"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker exec -t -i $(PROJECT_ID)-db /bin/bash -c "PGPASSWORD=docker pg_dump -Fc -h localhost -U docker -f /tmp/$(PROJECT_ID)-snapshot.dmp gis"
|
||||
@docker cp $(PROJECT_ID)-db:/tmp/$(PROJECT_ID)-snapshot.dmp .
|
||||
@docker exec -t -i $(PROJECT_ID)-db /bin/bash -c "rm /tmp/$(PROJECT_ID)-snapshot.dmp"
|
||||
@ls -lahtr *.dmp
|
||||
|
||||
dbschema:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Print the database schema to stdio"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker exec -t -i $(PROJECT_ID)-db /bin/bash -c "PGPASSWORD=docker pg_dump -s -h localhost -U docker gis"
|
||||
|
||||
dbshell:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Shelling in in production database"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker exec -t -i $(PROJECT_ID)-db psql -U docker -h localhost gis
|
||||
|
||||
dbrestore:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Restore dump from backups/latest.dmp in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@# - prefix causes command to continue even if it fails
|
||||
-@docker exec -t -i $(PROJECT_ID)-db su - postgres -c "dropdb gis"
|
||||
@docker exec -t -i $(PROJECT_ID)-db su - postgres -c "createdb -O docker -T template_postgis gis"
|
||||
@docker exec -t -i $(PROJECT_ID)-db pg_restore /backups/latest.dmp | docker exec -i $(PROJECT_ID)-db su - postgres -c "psql gis"
|
||||
|
||||
dbbackup:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Create `date +%d-%B-%Y`.dmp in production mode"
|
||||
@echo "Warning: backups/latest.dmp will be replaced with a symlink to "
|
||||
@echo "the new backup."
|
||||
@echo "------------------------------------------------------------------"
|
||||
@# - prefix causes command to continue even if it fails
|
||||
@# Explicitly don't use -it so we can call this make target over a remote ssh session
|
||||
@docker exec $(PROJECT_ID)-db-backups /backups.sh
|
||||
@docker exec $(PROJECT_ID)-db-backups cat /var/log/cron.log | tail -2 | head -1 | awk '{print $4}'
|
||||
-@if [ -f "backups/latest.dmp" ]; then rm backups/latest.dmp; fi
|
||||
# backups is intentionally missing from front of first clause below otherwise symlink comes
|
||||
# out with wrong path...
|
||||
@ln -s `date +%Y`/`date +%B`/PG_$(PROJECT_ID)_gis.`date +%d-%B-%Y`.dmp backups/latest.dmp
|
||||
@echo "Backup should be at: backups/`date +%Y`/`date +%B`/PG_$(PROJECT_ID)_gis.`date +%d-%B-%Y`.dmp"
|
||||
|
||||
sentry:
|
||||
@echo
|
||||
@echo "--------------------------"
|
||||
@echo "Running sentry production mode"
|
||||
@echo "--------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) up -d sentry
|
||||
|
||||
maillogs:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Showing smtp logs in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker exec -t -i $(PROJECT_ID)-smtp tail -f /var/log/mail.log
|
||||
|
||||
mailerrorlogs:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Showing smtp error logs in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker exec -t -i $(PROJECT_ID)-smtp tail -f /var/log/mail.err
|
||||
|
||||
mediasync:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Syncing media files and secret.py from live server"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@rsync -av --progress $(PRODUCTION_HOST):/home/web/haitidata/deployment/media/ media
|
||||
@rsync -av --progress $(PRODUCTION_HOST):/home/web/haitidata/django_project/core/settings/secret.py ../django_project/core/settings/
|
||||
|
||||
dbsync:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Syncing database files from live server"
|
||||
@echo "------------------------------------------------------------------"
|
||||
-@ssh $(PRODUCTION_HOST) "cd /home/web/haitidata/deployment; make dbbackup"
|
||||
@rsync -av --progress $(PRODUCTION_HOST):/home/web/haitidata/deployment/backups/ backups
|
||||
|
||||
create-machine:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Creating a docker machine."
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-machine create -d virtualbox $(PROJECT_ID)
|
||||
|
||||
enable-machine:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Enabling docker machine."
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "eval \"$(docker-machine env mezzanine)\""
|
||||
|
||||
create-workspace:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Create geoserver workspace."
|
||||
@echo "------------------------------------------------------------------"
|
||||
@curl -v -u admin:geoserver -XPOST -H "Content-type: text/xml" -d "<workspace><name>geonode</name><default>true</default></workspace>" http://localhost:33308/geoserver/rest/workspaces
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# DEVELOPMENT C O M M A N D S
|
||||
# --no-deps will attach to prod deps if running
|
||||
# after running you will have ssh and web ports open (see dockerfile for no's)
|
||||
# and you can set your pycharm to use the python in the container
|
||||
# Note that pycharm will copy in resources to the /root/ user folder
|
||||
# for pydevd etc. If they dont get copied, restart pycharm...
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
devweb: db
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running in DEVELOPMENT mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) up --no-deps -d devweb
|
||||
|
||||
|
||||
build-devweb: db
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Building devweb"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) build devweb
|
||||
|
||||
|
||||
# --------------- help --------------------------------
|
||||
|
||||
help:
|
||||
@echo "* **build** - builds all required containers."
|
||||
@echo "* **build-devweb** - build the development container. See [development notes](README-dev.md)."
|
||||
@echo "* **collectstatic** - run the django collectstatic command."
|
||||
@echo "* **create-machine** ."
|
||||
@echo "* **db** - build and run the db container."
|
||||
@echo "* **dbbackup** - make a snapshot of the database, saving it to deployments/backups/YYYY/MM/project-DDMMYYYY.dmp. It also creates a symlink to backups/latest.dmp for the latest backup."
|
||||
@echo "* **dbbash** - open a bash shell inside the database container."
|
||||
@echo "* **dblogs** - view the database logs."
|
||||
@echo "* **dbrestore** - restore deployment/backups/latest.dmp over the active database. Will delete any existing data in your database and replace with the restore, so **use with caution**."
|
||||
@echo "* **dbschema** - dump the current db schema (without data) to stdio. Useful if you want to compare changes between instances."
|
||||
@echo "* **dbshell** - get a psql prompt into the db container. "
|
||||
@echo "* **dbsnapshot** - as above but makes the backup as deployment/snapshot.smp - replacing any pre-existing snapshot."
|
||||
@echo "* **dbsync** - use this from a development or offsite machine. It will rsync all database backups from deployment/backups to your offsite machine."
|
||||
@echo "* **default** ."
|
||||
@echo "* **deploy** ."
|
||||
@echo "* **devweb** - create an ssh container derived from uwsgi that can be used as a remote interpreter for PyCharm. See [development notes](README-dev.md)."
|
||||
@echo "* **enable-machine** - "
|
||||
@echo "* **kill** - kills all running containers. Does not remove them."
|
||||
@echo "* **logs** - view the logs of all running containers. Note that you can also view individual logs in the deployment/logs directory."
|
||||
@echo "* **mailerrorlogs** - View the error logs from the mail server."
|
||||
@echo "* **maillogs** - view the transaction logs from the mail server."
|
||||
@echo "* **mediasync** - use this from a development or offsite machine. It will rsync all media backups from deployment/media to your offsite machine."
|
||||
@echo "* **migrate** - run any pending migrations. "
|
||||
@echo "* **nginx** - builds and runs the nginx container."
|
||||
@echo "* **nginxlogs** - view just the nginx activity logs."
|
||||
@echo "* **permissions** - Update the permissions of shared volumes. Note this will destroy any existing permissions you have in place."
|
||||
@echo "* **reload** - reload the uwsgi process. Useful when you need django to pick up any changes you may have deployed."
|
||||
@echo "* **rm** - remove all containers."
|
||||
@echo "* **rm-only** - remove any containers without trying to kill them first. "
|
||||
@echo "* **run** - builds and runs the complete orchestrated set of containers."
|
||||
@echo "* **sentry** - **currently not working I think.** The idea is to spin up a sentry instance together with your app for fault reporting."
|
||||
@echo "* **shell** - open a bash shell in the uwsgi (where django runs) container."
|
||||
@echo "* **superuser** - create a django superuser account."
|
||||
@echo "* **update-migrations** - freshen all migration definitions to match the current code base."
|
||||
@echo "* **web** - same as **run** - runs the production site."
|
||||
|
||||
worker:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Running worker production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) up -d worker
|
||||
|
||||
workerlogs:
|
||||
@echo
|
||||
@echo "------------------------------------------------------------------"
|
||||
@echo "Showing celery worker logs in production mode"
|
||||
@echo "------------------------------------------------------------------"
|
||||
@docker-compose -p $(PROJECT_ID) logs worker
|
||||
@@ -0,0 +1,99 @@
|
||||
# Deploying with docker
|
||||
|
||||
**Note:** This documentation is intentionally generic so that it can
|
||||
be copy-pasted between projects - do not put project specific details here.
|
||||
|
||||
This document explains how to do various sysadmin related tasks when your
|
||||
site has been deployed under docker. These deployment modes are supported:
|
||||
|
||||
* **production**: no debug etc is enabled, has its own discrete database. Configure
|
||||
your production environment in core.settings.prod_docker - this
|
||||
DJANGO_SETTINGS_MODULE is used when running in production mode.
|
||||
* **staging**: Configure your staging environment in core.settings.staging_docker -
|
||||
this DJANGO_SETTINGS_MODULE is used when running in production mode.
|
||||
|
||||
# Build your docker images and run them
|
||||
|
||||
## Production
|
||||
|
||||
You can simply run the provided script and it will build and deploy the docker
|
||||
images for you in **production mode**.
|
||||
|
||||
```
|
||||
cd deployment
|
||||
# allow pg volume to be written to
|
||||
sudo chmod -R a+rwX pg/postgres_data/
|
||||
make deploy
|
||||
sudo chmod -R a+rwX static
|
||||
```
|
||||
|
||||
Now point your browser at the ip of the web container on port 8080 or to the
|
||||
host port mapping as defined in the fig.yml file.
|
||||
|
||||
|
||||
To make a superuser account do:
|
||||
|
||||
```
|
||||
make shell
|
||||
python manage.py createsuperuser
|
||||
exit
|
||||
```
|
||||
|
||||
## Staging
|
||||
|
||||
The procedure is exactly the same as production, but you should preceed
|
||||
each command with 'staging' e.g. ``make staging-deploy``.
|
||||
|
||||
**Note:** VERY IMPORTANT - for staging deployment you should use a **separate
|
||||
git checkout** from the production checkout as the code from the git checkout
|
||||
is shared into the source tree.
|
||||
|
||||
## Using make
|
||||
|
||||
The following key make commands are provided for production:
|
||||
|
||||
* **build** - build production containers
|
||||
* **run** - builds then runs db and uwsgi services
|
||||
* **collectstatic** - collect static in production instance
|
||||
* **migrate** - run django migrations in production instance
|
||||
|
||||
Additional make commands are provided in the Makefile - please see there
|
||||
for details.
|
||||
|
||||
#### Arbitrary commands
|
||||
|
||||
Running arbitrary management commands is easy
|
||||
|
||||
|
||||
## Setup nginx reverse proxy
|
||||
|
||||
You should create a new nginx virtual host - please see
|
||||
``*-nginx.conf`` in the deployment directory for examples. There is
|
||||
one provided for production and one for staging.
|
||||
|
||||
Simply add the example file (renaming them as needed) to your
|
||||
``/etc/nginx/sites-enabled/`` directory and then modify the contents to
|
||||
match your local filesystem paths. Then use
|
||||
|
||||
```
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
To verify that your configuration is correct and then reload / restart nginx
|
||||
e.g.
|
||||
|
||||
```
|
||||
sudo /etc/init.d/nginx restart
|
||||
```
|
||||
|
||||
|
||||
### Managing containers
|
||||
|
||||
Please refer to the general [fig documentation](http://www.fig.sh/cli.hyml)
|
||||
for further notes on how to manage the infrastructure using fig.
|
||||
|
||||
# Configuration options
|
||||
|
||||
You can configure the base port used and various other options like the
|
||||
image organisation namespace and postgis user/pass by editing the ``fig*.yml``
|
||||
files.
|
||||
@@ -0,0 +1,197 @@
|
||||
#
|
||||
# Production server with UWSGI configured to run on port 8080
|
||||
#
|
||||
# docker-compose build
|
||||
# docker-compose up -d web
|
||||
#
|
||||
# See accompanying Make commands for easy collectstatic etc.
|
||||
|
||||
rabbitmq:
|
||||
image: library/rabbitmq
|
||||
hostname: rabbitmq
|
||||
environment:
|
||||
- RABBIT_PASSWORD=rabbit_test_password
|
||||
- USER=rabbit_user
|
||||
- RABBITMQ_NODENAME=rabbit
|
||||
|
||||
|
||||
smtp:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-smtp
|
||||
image: catatnight/postfix
|
||||
hostname: postfix
|
||||
environment:
|
||||
# You could change this to something more suitable
|
||||
- maildomain=kartoza.com
|
||||
- smtp_user=noreply:docker
|
||||
restart: always
|
||||
|
||||
db:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-db
|
||||
image: kartoza/postgis:9.4-2.1
|
||||
volumes:
|
||||
- ./pg/postgres_data:/var/lib/postgresql
|
||||
- ./backups:/backups
|
||||
- ./fresh-backups:/fresh-backups
|
||||
environment:
|
||||
- USERNAME=docker
|
||||
- PASS=docker
|
||||
restart: always
|
||||
ports:
|
||||
- "33305:5432"
|
||||
|
||||
uwsgi-base:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
build: docker
|
||||
hostname: uwsgi
|
||||
environment:
|
||||
- DATABASE_NAME=gis
|
||||
- DATABASE_USERNAME=docker
|
||||
- DATABASE_PASSWORD=docker
|
||||
- DATABASE_HOST=db
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
- VIRTUAL_HOST=haitidata.com
|
||||
- VIRTUAL_PORT=8080
|
||||
- GEOSERVER_PUBLIC_LOCATION=http://0.0.0.0:33300/api/geoserver/
|
||||
# Set your locale - you need to ensure this is created in Dockerfile too...
|
||||
- LC_ALL=en_ZA.UTF-8
|
||||
volumes:
|
||||
- ../django_project:/home/web/django_project
|
||||
- ./static:/home/web/static:rw
|
||||
- ./media:/home/web/media:rw
|
||||
- ./reports:/home/web/reports
|
||||
- ./logs:/var/log/
|
||||
user: root
|
||||
|
||||
uwsgi-ephemeral:
|
||||
extends: uwsgi-base
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
user: root
|
||||
restart: on-failure:5
|
||||
|
||||
uwsgi:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
extends: uwsgi-base
|
||||
container_name: haitidata-uwsgi
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
- geoserver:geoserver
|
||||
restart: always
|
||||
|
||||
dbbackups:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-db-backups
|
||||
image: kartoza/pg-backup:9.4
|
||||
hostname: pg-backups
|
||||
volumes:
|
||||
- ./backups:/backups
|
||||
links:
|
||||
- db:db
|
||||
environment:
|
||||
# take care to let the project name below match that
|
||||
# declared in the top of the makefile
|
||||
- DUMPPREFIX=PG_haiti
|
||||
# These are all defaults anyway, but setting explicitly in
|
||||
# case we ever want to ever use different credentials
|
||||
- PGUSER=docker
|
||||
- PGPASSWORD=docker
|
||||
- PGPORT=5432
|
||||
- PGHOST=db
|
||||
- PGDATABASE=gis
|
||||
restart: always
|
||||
|
||||
# This is normally the main entry point for a production server
|
||||
web:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-web
|
||||
image: nginx
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./sites-enabled:/etc/nginx/conf.d:ro
|
||||
# I dont use volumes_from as I want to use the ro modifier
|
||||
- ./static:/home/web/static:ro
|
||||
- ./media:/home/web/media:ro
|
||||
- ./logs:/var/log/nginx
|
||||
# - /etc/letsencrypt:/etc/letsencrypt
|
||||
- ./webroot-path:/home/web/webroot-path
|
||||
links:
|
||||
- uwsgi:uwsgi
|
||||
- geoserver:geoserver
|
||||
ports:
|
||||
- "33300:8080"
|
||||
restart: always
|
||||
|
||||
# This is the entry point for a development server.
|
||||
# Run with --no-deps to run attached to the services
|
||||
# from produ environment if wanted
|
||||
devweb:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-dev-web
|
||||
build: docker
|
||||
dockerfile: Dockerfile-dev
|
||||
hostname: haitidata-dev-web
|
||||
environment:
|
||||
- DATABASE_NAME=gis
|
||||
- DATABASE_USERNAME=docker
|
||||
- DATABASE_PASSWORD=docker
|
||||
- DATABASE_HOST=db
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
- PYTHONPATH=/home/web/django_project
|
||||
- VIRTUAL_HOST=haitidata.com
|
||||
- VIRTUAL_PORT=8080
|
||||
- GEOSERVER_PUBLIC_LOCATION=http://0.0.0.0:33303/api/geoserver/
|
||||
volumes:
|
||||
- ../django_project:/home/web/django_project
|
||||
- ./static:/home/web/static
|
||||
- ./media:/home/web/media
|
||||
- ./reports:/home/web/reports
|
||||
- ./logs:/var/log/
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
- rabbitmq:rabbitmq
|
||||
- geoserver:geoserver
|
||||
ports:
|
||||
# for django test server
|
||||
- "33303:8080"
|
||||
# for ssh
|
||||
- "33302:22"
|
||||
|
||||
worker:
|
||||
build: docker
|
||||
hostname: worker
|
||||
command: celery worker -A config.celery -l info --beat
|
||||
environment:
|
||||
- DATABASE_NAME=gis
|
||||
- DATABASE_USERNAME=docker
|
||||
- DATABASE_PASSWORD=docker
|
||||
- DATABASE_HOST=db
|
||||
- DATABASE_QGIS_HOST=db_gis
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
- VIRTUAL_HOST=haitidata.com
|
||||
- VIRTUAL_PORT=8080
|
||||
- C_FORCE_ROOT=true
|
||||
volumes:
|
||||
- ../django_project:/home/web/django_project
|
||||
- ./static:/home/web/static
|
||||
- ./media:/home/web/media
|
||||
- ./reports:/home/web/reports
|
||||
- ./logs:/var/log/
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
- rabbitmq:rabbitmq
|
||||
restart: on-failure:5
|
||||
|
||||
geoserver:
|
||||
image: kartoza/geoserver
|
||||
container_name: haitidata-geoserver
|
||||
ports:
|
||||
- "33308:8080"
|
||||
@@ -0,0 +1,4 @@
|
||||
#Acquire::http { Proxy "http://192.168.0.106:3142"; };
|
||||
#Acquire::http { Proxy "http://192.168.2.9:3142"; };
|
||||
#Acquire::http { Proxy "http://192.168.0.131:3142"; };
|
||||
#Acquire::http { Proxy "http://192.168.1.13:3142"; };
|
||||
@@ -0,0 +1,60 @@
|
||||
#--------- Generic stuff all our Dockerfiles should start with so we get caching ------------
|
||||
# Note this base image is based on debian
|
||||
FROM kartoza/django-base
|
||||
MAINTAINER Muhammad Anis<anis@kartoza.com>
|
||||
|
||||
#RUN ln -s /bin/true /sbin/initctl
|
||||
|
||||
# Use local cached debs from host (saves your bandwidth!)
|
||||
# Change ip below to that of your apt-cacher-ng host
|
||||
# Or comment this line out if you do not with to use caching
|
||||
ADD 71-apt-cacher-ng /etc/apt/apt.conf.d/71-apt-cacher-ng
|
||||
|
||||
# Pandoc needed to generate rst dumps, uic compressor needed for django-pipeline
|
||||
RUN apt-get update -y
|
||||
|
||||
# Set the locale - change to your preferred locale ... We use South African english
|
||||
# We do this as cartridge (ecommerce platform for mezzanine) relies on
|
||||
# the locale to set the correct number of decimal places for the MoneyField
|
||||
RUN echo "deb http://ftp.debian.org/debian jessie main" >> /etc/apt/sources.list.d/gdal.list
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install locales && \
|
||||
echo "en_ZA.UTF-8 UTF-8" >> /etc/locale.gen && \
|
||||
locale-gen en_ZA.UTF-8
|
||||
RUN apt-get install libgdal-dev -y
|
||||
ENV LANG en_ZA.UTF-8
|
||||
ENV LANGUAGE en_ZA:en
|
||||
ENV LC_ALL en_ZA.UTF-8
|
||||
ENV CPLUS_INCLUDE_PATH /usr/include/gdal
|
||||
ENV C_INCLUDE_PATH /usr/include/gdal
|
||||
ENV TEST a
|
||||
|
||||
ADD REQUIREMENTS.txt /REQUIREMENTS.txt
|
||||
RUN pip install -r /REQUIREMENTS.txt
|
||||
RUN pip uninstall -y South
|
||||
RUN pip install uwsgi
|
||||
|
||||
RUN pip uninstall geonode-dialogos -y
|
||||
RUN pip install git+https://github.com/meomancer/geonode-dialogos.git
|
||||
|
||||
RUN pip uninstall geonode-agon-ratings -y
|
||||
RUN pip install git+https://github.com/euclidian/custom-agon-ratings
|
||||
|
||||
RUN pip uninstall Django -y
|
||||
RUN pip install Django==1.8.18
|
||||
|
||||
RUN pip uninstall django-oauth-toolkit -y
|
||||
RUN pip install django-oauth-toolkit==0.12.0
|
||||
|
||||
RUN pip uninstall django-user-accounts -y
|
||||
RUN pip install git+https://github.com/GeoNode/geonode-user-accounts@invite-user
|
||||
|
||||
RUN apt-get install gdal-bin
|
||||
# Open port 8080 as we will be running our uwsgi socket on that
|
||||
EXPOSE 8080
|
||||
|
||||
#USER www-data
|
||||
|
||||
WORKDIR /home/web/django_project
|
||||
CMD ["uwsgi", "--env", "LC_ALL=en_ZA.UTF-8", "--ini", "/uwsgi.conf"]
|
||||
@@ -0,0 +1,68 @@
|
||||
#--------- Generic stuff all our Dockerfiles should start with so we get caching ------------
|
||||
# Note this base image is based on debian
|
||||
FROM kartoza/django-base
|
||||
MAINTAINER Muhammad Anis<anis@kartoza.com>
|
||||
|
||||
#RUN ln -s /bin/true /sbin/initctl
|
||||
|
||||
# Use local cached debs from host (saves your bandwidth!)
|
||||
# Change ip below to that of your apt-cacher-ng host
|
||||
# Or comment this line out if you do not with to use caching
|
||||
# ADD 71-apt-cacher-ng /etc/apt/apt.conf.d/71-apt-cacher-ng
|
||||
|
||||
# Pandoc needed to generate rst dumps, uic compressor needed for django-pipeline
|
||||
RUN apt-get update -y
|
||||
|
||||
# Set the locale - change to your preferred locale ... We use South African english
|
||||
# We do this as cartridge (ecommerce platform for mezzanine) relies on
|
||||
# the locale to set the correct number of decimal places for the MoneyField
|
||||
RUN echo "deb http://ftp.debian.org/debian jessie main" >> /etc/apt/sources.list.d/gdal.list
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install locales && \
|
||||
echo "en_ZA.UTF-8 UTF-8" >> /etc/locale.gen && \
|
||||
locale-gen en_ZA.UTF-8
|
||||
RUN apt-get install libgdal-dev -y
|
||||
ENV LANG en_ZA.UTF-8
|
||||
ENV LANGUAGE en_ZA:en
|
||||
ENV LC_ALL en_ZA.UTF-8
|
||||
ENV CPLUS_INCLUDE_PATH /usr/include/gdal
|
||||
ENV C_INCLUDE_PATH /usr/include/gdal
|
||||
|
||||
ADD REQUIREMENTS.txt /REQUIREMENTS.txt
|
||||
RUN pip install -r /REQUIREMENTS.txt
|
||||
RUN pip uninstall -y South
|
||||
RUN pip install uwsgi
|
||||
|
||||
RUN pip uninstall geonode-dialogos -y
|
||||
RUN pip install git+https://github.com/meomancer/geonode-dialogos.git
|
||||
|
||||
RUN pip uninstall geonode-agon-ratings -y
|
||||
RUN pip install git+https://github.com/euclidian/custom-agon-ratings
|
||||
|
||||
RUN pip uninstall Django -y
|
||||
RUN pip install Django==1.8.18
|
||||
|
||||
RUN pip uninstall django-oauth-toolkit -y
|
||||
RUN pip install django-oauth-toolkit==0.12.0
|
||||
|
||||
RUN pip uninstall django-user-accounts -y
|
||||
RUN pip install git+https://github.com/GeoNode/geonode-user-accounts@invite-user
|
||||
|
||||
RUN apt-get install gdal-bin
|
||||
# Open port 8080 as we will be running our uwsgi socket on that
|
||||
EXPOSE 8080
|
||||
|
||||
#USER www-data
|
||||
|
||||
WORKDIR /home/web
|
||||
|
||||
# Download repository
|
||||
RUN git clone https://github.com/euclidian/haitidata
|
||||
|
||||
RUN make migrate
|
||||
RUN make collectstatic
|
||||
|
||||
WORKDIR /home/web/django_project
|
||||
|
||||
CMD ["uwsgi", "--env", "LC_ALL=en_ZA.UTF-8", "--ini", "/uwsgi.conf"]
|
||||
@@ -0,0 +1,41 @@
|
||||
#--------- Generic stuff all our Dockerfiles should start with so we get caching ------------
|
||||
# Note this base image is based on debian
|
||||
FROM haitidata_uwsgi
|
||||
MAINTAINER Muhammad Anis<anis@kartoza.com>
|
||||
|
||||
# Use local cached debs from host (saves your bandwidth!)
|
||||
# Change ip below to that of your apt-cacher-ng host
|
||||
# Or comment this line out if you do not with to use caching
|
||||
ADD 71-apt-cacher-ng /etc/apt/apt.conf.d/71-apt-cacher-ng
|
||||
|
||||
|
||||
# This section taken on 2 July 2015 from
|
||||
# https://docs.docker.com/examples/running_ssh_service/
|
||||
# Sudo is needed by pycharm when it tries to pip install packages
|
||||
RUN apt-get update && apt-get install -y openssh-server sudo
|
||||
RUN mkdir /var/run/sshd
|
||||
RUN echo 'root:docker' | chpasswd
|
||||
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
|
||||
|
||||
# SSH login fix. Otherwise user is kicked off after login
|
||||
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
|
||||
|
||||
ENV NOTVISIBLE "in users profile"
|
||||
RUN echo "export VISIBLE=now" >> /etc/profile
|
||||
|
||||
# End of cut & paste section
|
||||
|
||||
ADD REQUIREMENTS-dev.txt /REQUIREMENTS-dev.txt
|
||||
RUN pip install -r /REQUIREMENTS-dev.txt
|
||||
ADD bashrc /root/.bashrc
|
||||
|
||||
# --------------------------------------------------------
|
||||
# Open ports as needed
|
||||
# --------------------------------------------------------
|
||||
|
||||
# Open port 8080 as we will be running our django dev server on
|
||||
EXPOSE 8080
|
||||
# Open port 22 as we will be using a remote interpreter from pycharm
|
||||
EXPOSE 22
|
||||
|
||||
CMD ["/usr/sbin/sshd", "-D"]
|
||||
@@ -0,0 +1,15 @@
|
||||
django-nose
|
||||
coverage
|
||||
pep8>=1.5.7,<1.6
|
||||
pylint
|
||||
flake8==2.5.4
|
||||
factory_boy
|
||||
|
||||
django-devserver
|
||||
|
||||
# documentation
|
||||
Sphinx
|
||||
|
||||
# pdb plus plus
|
||||
pdbpp
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
celery==3.1
|
||||
Django==1.10
|
||||
psycopg2
|
||||
django-compressor
|
||||
icalendar==3.0.1b2
|
||||
geopy==0.95.1
|
||||
filebrowser-safe==0.4.5
|
||||
# https://github.com/getsentry/raven-python/issues/337
|
||||
raven==5.8.1
|
||||
django-sentry==1.13.5
|
||||
requests==2.13.0
|
||||
|
||||
#cartridge requirement
|
||||
cartridge==0.12.0
|
||||
|
||||
|
||||
django-appconf==1.0.1
|
||||
django-model-utils==2.3.1
|
||||
django-reversion==1.9.3
|
||||
django-sitetree==1.5.1
|
||||
django-taggit==0.21.5
|
||||
django-timezone-field==1.3
|
||||
django-user-accounts==1.2.0
|
||||
easy-thumbnails==2.2
|
||||
html5lib==0.999999999
|
||||
markdown==2.6.2
|
||||
pytz==2015.6
|
||||
pyyaml==3.11
|
||||
ruamel.yaml==0.13.14
|
||||
bleach==2.0.0
|
||||
|
||||
|
||||
#geonode
|
||||
django-geonode-client
|
||||
git+https://github.com/GeoNode/django-osgeo-importer.git
|
||||
django-jsonfield
|
||||
|
||||
|
||||
python-dateutil
|
||||
gdal==1.10.0
|
||||
git+https://github.com/meomancer/GeoNode-1.git
|
||||
django-tastypie
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This is intended to be placed in the docker dev environment so
|
||||
# that the django project is in the path when you log in
|
||||
|
||||
if [ -f /etc/bashrc ]; then
|
||||
. /etc/bashrc
|
||||
fi
|
||||
export PYTHONPATH=/home/web/django_project:$PYTHONPATH
|
||||
export DJANGO_SETTINGS_MODULE=core.settings.dev_docker
|
||||
cd /home/web/django_project
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
docker build -t kartoza/mezzanine .
|
||||
@@ -0,0 +1,19 @@
|
||||
[uwsgi]
|
||||
|
||||
# Touch this file to reload uwsgi
|
||||
touch-reload = /tmp/touch-me-to-reload
|
||||
chdir = /home/web/django_project
|
||||
module = core.wsgi
|
||||
master = true
|
||||
pidfile=/tmp/django.pid
|
||||
socket = 0.0.0.0:49360
|
||||
workers = 4
|
||||
cheaper = 2
|
||||
env = DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
# disabled so we run in the foreground for docker
|
||||
#daemonize = /tmp/django.log
|
||||
reload-os-env
|
||||
#uid = 1000
|
||||
#gid = 1000
|
||||
memory-report = true
|
||||
harakiri = 20
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
from locust import HttpLocust, TaskSet, task
|
||||
|
||||
|
||||
class HaitiData(TaskSet):
|
||||
def on_start(self):
|
||||
response = self.client.get("/account/login")
|
||||
csrftoken = response.cookies['csrftoken']
|
||||
self.client.post("/account/login/",
|
||||
{"username": "admin",
|
||||
"password": "admin"},
|
||||
headers={"X-CSRFToken": csrftoken})
|
||||
|
||||
@task
|
||||
def index(self):
|
||||
self.client.get("/layers/geonode:madagascar_earthquake_cqmtpiu_tif_2017_08_23_08_59_10")
|
||||
|
||||
|
||||
class HaitiUser(HttpLocust):
|
||||
task_set = HaitiData
|
||||
min_wait = 5000
|
||||
max_wait = 15000
|
||||
Regular → Executable
File renamed without changes.
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
MYDATE=`date +%d-%B-%Y`
|
||||
MONTH=$(date +%B)
|
||||
YEAR=$(date +%Y)
|
||||
cd /home/web/mezzanine.kartoza.com/deployment
|
||||
MYBASEDIR=`pwd`/backups
|
||||
MYBACKUPDIR=${MYBASEDIR}/${YEAR}/${MONTH}
|
||||
mkdir -p ${MYBACKUPDIR}
|
||||
|
||||
cd ${MYBACKUPDIR}
|
||||
docker exec -ti mezzanine-db /bin/bash -c "PGPASSWORD=docker pg_dump -Fc -f /tmp/latest.dmp -h localhost -U docker gis"
|
||||
docker cp mezzanine-db:/tmp/latest.dmp PG_mezzanine-${MYDATE}.dmp
|
||||
|
||||
cd -
|
||||
rm backups/latest.dmp
|
||||
|
||||
cd backups
|
||||
ln -s ${MYBACKUPDIR}/PG_mezzanine-${MYDATE}.dmp latest.dmp
|
||||
|
||||
cd ..
|
||||
File renamed without changes.
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- hosts: haitidata_swarm
|
||||
vars:
|
||||
http_port: 80
|
||||
max_clients: 200
|
||||
tasks:
|
||||
- name: pull haiti source code from github
|
||||
git:
|
||||
repo: https://github.com/euclidian/haitidata.git
|
||||
dest: /tmp/web_haiti
|
||||
- name: run migrate
|
||||
make:
|
||||
chdir: /tmp/web_haiti/deployment
|
||||
target: migrate
|
||||
- name: run collect static
|
||||
make:
|
||||
chdir: /tmp/web_haiti/deployment
|
||||
target: collectstatic
|
||||
@@ -0,0 +1,2 @@
|
||||
[haitidata_swarm]
|
||||
haitidata
|
||||
@@ -0,0 +1,53 @@
|
||||
upstream qgisug {
|
||||
server 127.0.0.1:63300;
|
||||
}
|
||||
|
||||
server {
|
||||
# OTF gzip compression
|
||||
gzip on;
|
||||
gzip_min_length 860;
|
||||
gzip_comp_level 5;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain application/xml application/x-javascript text/xml text/css application/json;
|
||||
gzip_disable “MSIE [1-6].(?!.*SV1)”;
|
||||
|
||||
# the port your site will be served on
|
||||
listen 443 ssl;
|
||||
# the domain name it will serve for
|
||||
server_name qgis.org.za;
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /etc/letsencrypt/live/qgis.org.za/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/qgis.org.za/privkey.pem;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
|
||||
|
||||
location /.well-known/acme-challenge {
|
||||
default_type "text/plain";
|
||||
# set to webroot path
|
||||
root /home/web/qgis.org.za/deployment/webroot-path;
|
||||
}
|
||||
|
||||
charset utf-8;
|
||||
|
||||
# max upload size, adjust to taste
|
||||
client_max_body_size 15M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://qgisug;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name qgis.org.za, foss4g-africa.org, www.foss4g-africa.org;
|
||||
if ($host = 'www.foss4g-africa.org') {
|
||||
return 301 https://foss4g-africa.org$request_uri;
|
||||
}
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{rabbit, [
|
||||
{default_user, <<“rabbit_admin”>>},
|
||||
{default_pass, <<“rabbit_password”>>},
|
||||
{tcp_listeners,[{“0.0.0.0″,5672}]}
|
||||
]}
|
||||
]
|
||||
@@ -0,0 +1,2 @@
|
||||
uwsgi:
|
||||
scale: 3
|
||||
@@ -0,0 +1,197 @@
|
||||
#
|
||||
# Production server with UWSGI configured to run on port 8080
|
||||
#
|
||||
# docker-compose build
|
||||
# docker-compose up -d web
|
||||
#
|
||||
# See accompanying Make commands for easy collectstatic etc.
|
||||
|
||||
rabbitmq:
|
||||
image: library/rabbitmq
|
||||
hostname: rabbitmq
|
||||
environment:
|
||||
- RABBIT_PASSWORD=rabbit_test_password
|
||||
- USER=rabbit_user
|
||||
- RABBITMQ_NODENAME=rabbit
|
||||
|
||||
|
||||
smtp:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-smtp
|
||||
image: catatnight/postfix
|
||||
hostname: postfix
|
||||
environment:
|
||||
# You could change this to something more suitable
|
||||
- maildomain=kartoza.com
|
||||
- smtp_user=noreply:docker
|
||||
restart: always
|
||||
|
||||
db:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-db
|
||||
image: kartoza/postgis:9.4-2.1
|
||||
volumes:
|
||||
- ./pg/postgres_data:/var/lib/postgresql
|
||||
- ./backups:/backups
|
||||
- ./fresh-backups:/fresh-backups
|
||||
environment:
|
||||
- USERNAME=docker
|
||||
- PASS=docker
|
||||
restart: always
|
||||
ports:
|
||||
- "33305:5432"
|
||||
|
||||
uwsgi-ephemeral:
|
||||
image: muhammadanis/haitidata_uwsgi
|
||||
environment:
|
||||
- DATABASE_NAME=gis
|
||||
- DATABASE_USERNAME=docker
|
||||
- DATABASE_PASSWORD=docker
|
||||
- DATABASE_HOST=db
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
- VIRTUAL_HOST=haitidata.com
|
||||
- VIRTUAL_PORT=8080
|
||||
- GEOSERVER_PUBLIC_LOCATION=http://0.0.0.0:33300/api/geoserver/
|
||||
# Set your locale - you need to ensure this is created in Dockerfile too...
|
||||
- LC_ALL=en_ZA.UTF-8
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
user: root
|
||||
restart: on-failure:5
|
||||
|
||||
uwsgi:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
image: muhammadanis/haitidata_uwsgi
|
||||
environment:
|
||||
- DATABASE_NAME=gis
|
||||
- DATABASE_USERNAME=docker
|
||||
- DATABASE_PASSWORD=docker
|
||||
- DATABASE_HOST=db
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
- VIRTUAL_HOST=haitidata.com
|
||||
- VIRTUAL_PORT=8080
|
||||
- GEOSERVER_PUBLIC_LOCATION=http://0.0.0.0:33300/api/geoserver/
|
||||
# Set your locale - you need to ensure this is created in Dockerfile too...
|
||||
- LC_ALL=en_ZA.UTF-8
|
||||
user: root
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
- geoserver:geoserver
|
||||
restart: always
|
||||
|
||||
dbbackups:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-db-backups
|
||||
image: kartoza/pg-backup:9.4
|
||||
hostname: pg-backups
|
||||
volumes:
|
||||
- ./backups:/backups
|
||||
links:
|
||||
- db:db
|
||||
environment:
|
||||
# take care to let the project name below match that
|
||||
# declared in the top of the makefile
|
||||
- DUMPPREFIX=PG_haiti
|
||||
# These are all defaults anyway, but setting explicitly in
|
||||
# case we ever want to ever use different credentials
|
||||
- PGUSER=docker
|
||||
- PGPASSWORD=docker
|
||||
- PGPORT=5432
|
||||
- PGHOST=db
|
||||
- PGDATABASE=gis
|
||||
restart: always
|
||||
|
||||
# This is normally the main entry point for a production server
|
||||
web:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-web
|
||||
image: nginx
|
||||
hostname: nginx
|
||||
volumes:
|
||||
- ./sites-enabled:/etc/nginx/conf.d:ro
|
||||
# I dont use volumes_from as I want to use the ro modifier
|
||||
- ./static:/home/web/static:ro
|
||||
- ./media:/home/web/media:ro
|
||||
- ./logs:/var/log/nginx
|
||||
# - /etc/letsencrypt:/etc/letsencrypt
|
||||
- ./webroot-path:/home/web/webroot-path
|
||||
links:
|
||||
- uwsgi:uwsgi
|
||||
- geoserver:geoserver
|
||||
ports:
|
||||
- "33300:8080"
|
||||
restart: always
|
||||
|
||||
# This is the entry point for a development server.
|
||||
# Run with --no-deps to run attached to the services
|
||||
# from produ environment if wanted
|
||||
devweb:
|
||||
# Note you cannot scale if you use conteiner_name
|
||||
container_name: haitidata-dev-web
|
||||
build: docker
|
||||
dockerfile: Dockerfile-dev
|
||||
hostname: haitidata-dev-web
|
||||
environment:
|
||||
- DATABASE_NAME=gis
|
||||
- DATABASE_USERNAME=docker
|
||||
- DATABASE_PASSWORD=docker
|
||||
- DATABASE_HOST=db
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
- PYTHONPATH=/home/web/django_project
|
||||
- VIRTUAL_HOST=haitidata.com
|
||||
- VIRTUAL_PORT=8080
|
||||
- GEOSERVER_PUBLIC_LOCATION=http://0.0.0.0:33303/api/geoserver/
|
||||
volumes:
|
||||
- ../django_project:/home/web/django_project
|
||||
- ./static:/home/web/static
|
||||
- ./media:/home/web/media
|
||||
- ./reports:/home/web/reports
|
||||
- ./logs:/var/log/
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
- rabbitmq:rabbitmq
|
||||
- geoserver:geoserver
|
||||
ports:
|
||||
# for django test server
|
||||
- "33303:8080"
|
||||
# for ssh
|
||||
- "33302:22"
|
||||
|
||||
worker:
|
||||
build: docker
|
||||
hostname: worker
|
||||
command: celery worker -A config.celery -l info --beat
|
||||
environment:
|
||||
- DATABASE_NAME=gis
|
||||
- DATABASE_USERNAME=docker
|
||||
- DATABASE_PASSWORD=docker
|
||||
- DATABASE_HOST=db
|
||||
- DATABASE_QGIS_HOST=db_gis
|
||||
- RABBITMQ_HOST=rabbitmq
|
||||
- DJANGO_SETTINGS_MODULE=core.settings.prod_docker
|
||||
- VIRTUAL_HOST=haitidata.com
|
||||
- VIRTUAL_PORT=8080
|
||||
- C_FORCE_ROOT=true
|
||||
volumes:
|
||||
- ../django_project:/home/web/django_project
|
||||
- ./static:/home/web/static
|
||||
- ./media:/home/web/media
|
||||
- ./reports:/home/web/reports
|
||||
- ./logs:/var/log/
|
||||
links:
|
||||
- smtp:smtp
|
||||
- db:db
|
||||
- rabbitmq:rabbitmq
|
||||
restart: on-failure:5
|
||||
|
||||
geoserver:
|
||||
image: kartoza/geoserver
|
||||
container_name: haitidata-geoserver
|
||||
ports:
|
||||
- "33308:8080"
|
||||
@@ -0,0 +1,4 @@
|
||||
This directory is used to contain nginx configuration
|
||||
when using a non dockergen nginx configuration.
|
||||
|
||||
See docker-compose.yml to see how it is used.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Define connection details for connecting to django running in
|
||||
# a docker container.
|
||||
upstream uwsgi {
|
||||
server uwsgi:8080;
|
||||
}
|
||||
|
||||
upstream geoserver {
|
||||
server geoserver:8080;
|
||||
}
|
||||
|
||||
server {
|
||||
# OTF gzip compression
|
||||
gzip on;
|
||||
gzip_min_length 860;
|
||||
gzip_comp_level 5;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain application/xml application/x-javascript text/xml text/css application/json;
|
||||
gzip_disable “MSIE [1-6].(?!.*SV1)”;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
# the port your site will be served on
|
||||
listen 8080;
|
||||
# the domain name it will serve for
|
||||
# server_name staging.foss4g-africa.org;
|
||||
charset utf-8;
|
||||
|
||||
# max upload size, adjust to taste
|
||||
client_max_body_size 15M;
|
||||
# Django media
|
||||
location /media {
|
||||
# your Django project's media files - amend as required
|
||||
alias /home/web/media;
|
||||
expires 21d; # cache for 71 days
|
||||
}
|
||||
location /static {
|
||||
# your Django project's static files - amend as required
|
||||
alias /home/web/static;
|
||||
expires 21d; # cache for 21 days
|
||||
}
|
||||
|
||||
location /api/geoserver/ {
|
||||
proxy_pass http://geoserver/geoserver/;
|
||||
}
|
||||
# Finally, send all non-media requests to the Django server.
|
||||
location / {
|
||||
uwsgi_pass uwsgi;
|
||||
# the uwsgi_params file you installed needs to be passed with each
|
||||
# request.
|
||||
# the uwsgi_params need to be passed with each uwsgi request
|
||||
uwsgi_param QUERY_STRING $query_string;
|
||||
uwsgi_param REQUEST_METHOD $request_method;
|
||||
uwsgi_param CONTENT_TYPE $content_type;
|
||||
uwsgi_param CONTENT_LENGTH $content_length;
|
||||
|
||||
uwsgi_param REQUEST_URI $request_uri;
|
||||
uwsgi_param PATH_INFO $document_uri;
|
||||
uwsgi_param DOCUMENT_ROOT $document_root;
|
||||
uwsgi_param SERVER_PROTOCOL $server_protocol;
|
||||
uwsgi_param HTTPS $https if_not_empty;
|
||||
|
||||
uwsgi_param REMOTE_ADDR $remote_addr;
|
||||
uwsgi_param REMOTE_PORT $remote_port;
|
||||
uwsgi_param SERVER_PORT $server_port;
|
||||
uwsgi_param SERVER_NAME $server_name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
upstream qgisug {
|
||||
server 127.0.0.1:63300;
|
||||
}
|
||||
|
||||
server {
|
||||
# OTF gzip compression
|
||||
gzip on;
|
||||
gzip_min_length 860;
|
||||
gzip_comp_level 5;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain application/xml application/x-javascript text/xml text/css application/json;
|
||||
gzip_disable “MSIE [1-6].(?!.*SV1)”;
|
||||
|
||||
# the port your site will be served on
|
||||
listen 80
|
||||
# the domain name it will serve for
|
||||
# server_name staging.foss4g-africa.org;
|
||||
|
||||
charset utf-8;
|
||||
|
||||
# max upload size, adjust to taste
|
||||
client_max_body_size 15M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://qgisug;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
}
|
||||
}
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Use this to tag a version on your local"
|
||||
echo "machine, push the tag upstream and then"
|
||||
echo "deploy it to the remote"
|
||||
echo "e.g."
|
||||
echo "$0 version-1.2.3"
|
||||
VERSION=$1
|
||||
make dbbackup
|
||||
make mediasync
|
||||
git tag $VERSION
|
||||
git push --tags upstream
|
||||
ssh kartoza4 "cd /home/web/kartoza.com/deployment && git fetch --tags && git checkout $VERSION && make build && make kill && make web && make migrate && make collectstatic && make reload"
|
||||
@@ -0,0 +1,30 @@
|
||||
# Haitidata
|
||||
|
||||
You should write some docs, it's good for the soul.
|
||||
|
||||
# Docker installation
|
||||
|
||||
## Install docker
|
||||
This will install an Haitidata container
|
||||
|
||||
Makes sure to have docker-compose installed
|
||||
```
|
||||
sudo apt-get install python-pip
|
||||
sudo pip install docker-compose
|
||||
```
|
||||
|
||||
## Get project code
|
||||
Clone the repo:
|
||||
```
|
||||
git clone https://github.com/HaitiData/haitidata.git
|
||||
```
|
||||
|
||||
## Run project
|
||||
Run the docker container:
|
||||
```
|
||||
cd deployment
|
||||
make deploy
|
||||
make update-migrations-localization:
|
||||
```
|
||||
|
||||
then you can visit http://0.0.0.0:33300```
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Django settings for charts project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.8.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.8/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
from geonode.settings import *
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SITENAME = 'charts-prj'
|
||||
|
||||
LOCAL_ROOT = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '*u(5zzp_wjj!^!w4$h@e#jv!*y02z17lb8gu-l)j+*79p^r8%('
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = INSTALLED_APPS + (
|
||||
'charts_app',
|
||||
'wfs_harvest'
|
||||
)
|
||||
|
||||
WSGI_APPLICATION = 'charts-prj.wsgi.application'
|
||||
|
||||
# Additional directories which hold static files
|
||||
STATICFILES_DIRS.append(
|
||||
os.path.join(LOCAL_ROOT, "static"),
|
||||
)
|
||||
|
||||
# Location of url mappings
|
||||
ROOT_URLCONF = 'charts-prj.urls'
|
||||
|
||||
# Location of locale files
|
||||
LOCALE_PATHS = (
|
||||
os.path.join(LOCAL_ROOT, 'locale'),
|
||||
) + LOCALE_PATHS
|
||||
|
||||
TEMPLATES[0]['DIRS'].insert(0, os.path.join(LOCAL_ROOT, "templates"))
|
||||
|
||||
MAX_CSV_RECORDS = 20000
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
PROXY_ALLOWED_HOSTS = ['*']
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,24 @@
|
||||
{% load i18n %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#info" data-toggle="tab"><i class="fa fa-info-circle"></i>{% trans "Info" %}</a></li>
|
||||
{% if resource.polymorphic_ctype.name == 'layer' %}
|
||||
<li><a href="#attributes" data-toggle="tab"><i class="fa fa-bars"></i>{% trans "Attributes" %}</a></li>
|
||||
{% endif %}
|
||||
{% if resource.is_mosaic %}
|
||||
<li><a href="#granules" data-toggle="tab"><i class="fa fa-bars"></i>{% trans "Granules" %}</a></li>
|
||||
{% endif %}
|
||||
{% if resource.service_type == 'WFS' %}
|
||||
<li><a href="#charts" data-toggle="tab"><i class="fa fa-charts"></i>{% trans "Charts" %}</a></li>
|
||||
{% endif %}
|
||||
{% if SOCIAL_BUTTONS %}
|
||||
<li><a href="#share" data-toggle="tab"><i class="fa fa-share"></i>{% trans "Share" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a href="#rate" data-toggle="tab"><i class="fa fa-star"></i>{% trans "Ratings" %}</a></li>
|
||||
<li><a href="#comments" data-toggle="tab"><i class="fa fa-comment-o"></i> {% trans "Comments" %}</a></li>
|
||||
{% if GEOGIG_ENABLED and resource.link_set.geogig %}
|
||||
<li><a href="#geogig" data-toggle="tab"><i class="fa fa-code-fork"></i> {% trans "GeoGig" %}</a></li>
|
||||
{% endif %}
|
||||
{% if EXIF_ENABLED and resource.polymorphic_ctype.name == 'document' and exif_data %}
|
||||
<li><a href="#exif" data-toggle="tab"><i class="fa fa-photo"></i> {% trans "Exif" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
File renamed without changes.
@@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
{% block extra_head %}
|
||||
<link href="{{ STATIC_URL }}css/site_base.css" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
{% block extra_tab %}
|
||||
{% comment %}
|
||||
Add Tab for Third Party Apps
|
||||
<li>
|
||||
<a href="{{ PROJECT_ROOT }}app">App</a>
|
||||
</li>
|
||||
{% endcomment %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends 'index.html' %}
|
||||
{% load i18n %}
|
||||
{% comment %}
|
||||
This is where you can override the hero area block. You can simply modify the content below or replace it wholesale to meet your own needs.
|
||||
{% endcomment %}
|
||||
{% block hero %}
|
||||
<div class="jumbotron">
|
||||
<div class="container">
|
||||
<h1>{% trans "Welcome to the charts GeoNode" %}</h1>
|
||||
<p>{% blocktrans %}GeoNode is an open source platform for sharing geospatial data and maps.{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}If you have any questions about the software or service, join our <a href="https://groups.google.com/d/forum/geonode-users">mailing list</a>.{% endblocktrans %}</p>
|
||||
<span>{% trans 'Need help?' %} <a href="http://docs.geonode.org/en/latest/tutorials/users/quickstart/index.html">{% trans 'Getting Started' %}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Django settings for pippo project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.8.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.8/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '5i81&t!#h$4ie!-kg$&@5b-1j*)d9@(6)8g8o4kg*xw3i2e=@t'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'charts-prj.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'charts-prj.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.8/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
ANONYMOUS_USER_ID = 1
|
||||
|
||||
LOCAL_MEDIA_URL = ''
|
||||
API_INCLUDE_REGIONS_COUNT = ''
|
||||
HAYSTACK_SEARCH = ''
|
||||
RESOURCE_PUBLISHING = ''
|
||||
File renamed without changes.
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
WSGI config for charts project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "charts-prj.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
||||
File renamed without changes.
@@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Chart
|
||||
|
||||
|
||||
class ChartAdmin(admin.ModelAdmin):
|
||||
list_display = ['id', 'title']
|
||||
|
||||
|
||||
admin.site.register(Chart, ChartAdmin)
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('layers', '24_to_26'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Chart',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=128, blank=True)),
|
||||
('category', models.CharField(max_length=200)),
|
||||
('quantity', models.CharField(max_length=200)),
|
||||
('type', models.SmallIntegerField(default=0, choices=[(0, b'Bar chart'), (1, b'Pie chart'), (2, b'Donut chart')])),
|
||||
('aggr_type', models.SmallIntegerField(default=3, choices=[(0, b'Sum'), (1, b'Mean'), (2, b'Category count'), (3, b'Max'), (4, b'Min')])),
|
||||
('abstract', models.TextField(blank=True)),
|
||||
('layer', models.ForeignKey(to='layers.Layer')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
import charts_app.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('charts_app', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='chart',
|
||||
name='created_by',
|
||||
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='chart',
|
||||
name='layer',
|
||||
field=models.ForeignKey(to='layers.Layer', validators=[charts_app.models.validate_wfs]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='chart',
|
||||
name='type',
|
||||
field=models.SmallIntegerField(default=0, choices=[(0, b'Bar chart'), (1, b'Pie chart'), (2, b'Donut chart'), (3, b'Line chart')]),
|
||||
),
|
||||
]
|
||||
File renamed without changes.
@@ -0,0 +1,80 @@
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.forms import ModelForm, ValidationError as VE
|
||||
|
||||
from geonode.layers.models import Layer
|
||||
|
||||
from wfs_harvest.utils import get_featno, get_fields
|
||||
|
||||
|
||||
def validate_wfs(value):
|
||||
lyr = Layer.objects.get(pk=value)
|
||||
if lyr.service_type != 'WFS':
|
||||
raise ValidationError(_('%(lyr_title)s is not a WFS layer'),
|
||||
code='not_a_wfs',
|
||||
params={'lyr_title': lyr.title})
|
||||
|
||||
feat_no = get_featno(value)
|
||||
if feat_no > settings.MAX_CSV_RECORDS:
|
||||
raise ValidationError(_('%(lyr_title)s has too many features; '
|
||||
'chart functionality is not available '
|
||||
'for very large datasets'),
|
||||
code='too_many_records',
|
||||
params={'lyr_title': lyr.title})
|
||||
|
||||
|
||||
class Chart(models.Model):
|
||||
CHART_TYPES = (
|
||||
(0, 'Bar chart'),
|
||||
(1, 'Pie chart'),
|
||||
(2, 'Donut chart'),
|
||||
(3, 'Line chart')
|
||||
)
|
||||
|
||||
AGGREGATION_TYPES = (
|
||||
(0, 'Sum'),
|
||||
(1, 'Mean'),
|
||||
(2, 'Category count'),
|
||||
(3, 'Max'),
|
||||
(4, 'Min'),
|
||||
)
|
||||
|
||||
layer = models.ForeignKey(Layer, validators=[validate_wfs])
|
||||
title = models.CharField(max_length=128, blank=True)
|
||||
category = models.CharField(max_length=200)
|
||||
quantity = models.CharField(max_length=200)
|
||||
type = models.SmallIntegerField(choices=CHART_TYPES, default=0)
|
||||
aggr_type = models.SmallIntegerField(choices=AGGREGATION_TYPES, default=3)
|
||||
abstract = models.TextField(blank=True)
|
||||
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null = True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('chart_detail', kwargs={'pk': self.pk})
|
||||
|
||||
class ChartForm(ModelForm):
|
||||
def clean(self):
|
||||
cleaned_data = super(ChartForm, self).clean()
|
||||
layer = cleaned_data.get('layer')
|
||||
category = cleaned_data.get('category')
|
||||
quantity = cleaned_data.get('quantity')
|
||||
|
||||
categories, quantities = get_fields(layer)
|
||||
|
||||
if category not in categories:
|
||||
lyr = Layer.objects.get(pk=layer)
|
||||
raise VE(_('%(lyr_title)s does not contain the field %(category)s'),
|
||||
code='non_extant_category_field',
|
||||
params={'lyr_title': lyr.title, 'category': category})
|
||||
|
||||
if quantity not in quantities:
|
||||
lyr = Layer.objects.get(pk=layer)
|
||||
raise VE(_('%(lyr_title)s does not contain the field %(quantity)s'),
|
||||
code='non_extant_quantity_field',
|
||||
params={'lyr_title': lyr.title, 'quantity': quantity})
|
||||
|
||||
class Meta:
|
||||
model = Chart
|
||||
fields = '__all__'
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Donut Chart v1</title>
|
||||
<h1>Simple Donut Chart v1</h1>
|
||||
<h2>Zero values excluded</h2>
|
||||
<p>D3js version 4, use canvas</p>
|
||||
<canvas width="960" height="500"></canvas>
|
||||
<script src="//d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
|
||||
var canvas = document.querySelector("canvas"),
|
||||
context = canvas.getContext("2d");
|
||||
|
||||
var width = canvas.width,
|
||||
height = canvas.height,
|
||||
radius = Math.min(width, height) / 2;
|
||||
|
||||
var colors = ["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00", "#800000", "#8E388E",
|
||||
"#7171C6", "#7D9EC0", "#388E8E", "#71C671", "#8E8E38", "#C5C1AA", "#C67171"];
|
||||
|
||||
var arc = d3.arc()
|
||||
.outerRadius(radius - 10)
|
||||
.innerRadius(radius - 70)
|
||||
.context(context);
|
||||
|
||||
var labelArc = d3.arc()
|
||||
.outerRadius(radius - 40)
|
||||
.innerRadius(radius - 40)
|
||||
.context(context);
|
||||
|
||||
var pie = d3.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.area; });
|
||||
|
||||
context.translate(width / 2, height / 2);
|
||||
|
||||
d3.csv("{% url "get_csv" %}", function(d) {
|
||||
d.area = +d.area;
|
||||
if (d.area !== 0) {
|
||||
return d;}
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
var arcs = pie(data);
|
||||
|
||||
arcs.forEach(function(d, i) {
|
||||
context.beginPath();
|
||||
arc(d);
|
||||
context.fillStyle = colors[i];
|
||||
context.fill();
|
||||
});
|
||||
|
||||
context.beginPath();
|
||||
arcs.forEach(arc);
|
||||
context.strokeStyle = "#fff";
|
||||
context.stroke();
|
||||
|
||||
context.textAlign = "center";
|
||||
context.textBaseline = "middle";
|
||||
context.fillStyle = "#000";
|
||||
arcs.forEach(function(d) {
|
||||
var c = labelArc.centroid(d);
|
||||
context.fillText(d.data.name, c[0], c[1]);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<body></body>
|
||||
@@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Donut Chart v2</title>
|
||||
<h1>Simple Donut Chart v2</h1>
|
||||
<h2>Zero values excluded, label with ticks</h2>
|
||||
<p>D3js version 3, use svg</p>
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
width: 960px;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
path.slice{
|
||||
stroke-width:2px;
|
||||
}
|
||||
|
||||
polyline{
|
||||
opacity: .3;
|
||||
stroke: black;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<script src="http://d3js.org/d3.v3.min.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
var svg = d3.select("body")
|
||||
.append("svg")
|
||||
.append("g")
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "slices");
|
||||
svg.append("g")
|
||||
.attr("class", "labels");
|
||||
svg.append("g")
|
||||
.attr("class", "lines");
|
||||
|
||||
var width = 960,
|
||||
height = 500,
|
||||
radius = Math.min(width, height) / 2;
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function(d) {
|
||||
return d.area;
|
||||
});
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.outerRadius(radius * 0.8)
|
||||
.innerRadius(radius * 0.4);
|
||||
|
||||
var outerArc = d3.svg.arc()
|
||||
.innerRadius(radius * 0.9)
|
||||
.outerRadius(radius * 0.9);
|
||||
|
||||
d3.csv("{% url "get_csv" %}", function(d) {
|
||||
d.area = +d.area;
|
||||
if (d.area !== 0) {
|
||||
return d;}
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
var key = function(d){ return d.data.name};
|
||||
|
||||
var color = d3.scale.ordinal()
|
||||
.domain([key])
|
||||
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
|
||||
|
||||
|
||||
var slice = svg.select(".slices").selectAll("path.slice")
|
||||
.data(pie(data), key);
|
||||
|
||||
slice.enter()
|
||||
.insert("path")
|
||||
.style("fill", function(d) { return color(d.data.area); })
|
||||
.attr("class", "slice");
|
||||
|
||||
slice
|
||||
.transition().duration(1000)
|
||||
.attrTween("d", function(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
return arc(interpolate(t));
|
||||
};
|
||||
})
|
||||
|
||||
|
||||
|
||||
/* ------- TEXT LABELS -------*/
|
||||
|
||||
var text = svg.select(".labels").selectAll("text")
|
||||
.data(pie(data), key);
|
||||
|
||||
text.enter()
|
||||
.append("text")
|
||||
.attr("dy", ".35em")
|
||||
.text(function(d) {
|
||||
return d.data.name + ' - ' +d.data.area; ;
|
||||
});
|
||||
|
||||
function midAngle(d){
|
||||
return d.startAngle + (d.endAngle - d.startAngle)/2;
|
||||
}
|
||||
|
||||
text.transition().duration(1000)
|
||||
.attrTween("transform", function(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
var d2 = interpolate(t);
|
||||
var pos = outerArc.centroid(d2);
|
||||
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1);
|
||||
return "translate("+ pos +")";
|
||||
};
|
||||
})
|
||||
.styleTween("text-anchor", function(d){
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
var d2 = interpolate(t);
|
||||
return midAngle(d2) < Math.PI ? "start":"end";
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
/* ------- SLICE TO TEXT POLYLINES -------*/
|
||||
|
||||
var polyline = svg.select(".lines").selectAll("polyline")
|
||||
.data(pie(data), key);
|
||||
|
||||
polyline.enter()
|
||||
.append("polyline");
|
||||
|
||||
polyline.transition().duration(1000)
|
||||
.attrTween("points", function(d){
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
var d2 = interpolate(t);
|
||||
var pos = outerArc.centroid(d2);
|
||||
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
|
||||
return [arc.centroid(d2), outerArc.centroid(d2), pos];
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Pie Chart v1</title>
|
||||
<h1>Simple Pie Chart v1</h1>
|
||||
<h2>All data</h2>
|
||||
<p>D3js version 4, use svg</p>
|
||||
<style>
|
||||
|
||||
.arc text {
|
||||
font: 10px sans-serif;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
<svg width="960" height="500"></svg>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
|
||||
var svg = d3.select("svg"),
|
||||
width = +svg.attr("width"),
|
||||
height = +svg.attr("height"),
|
||||
radius = Math.min(width, height) / 2,
|
||||
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
var color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
|
||||
|
||||
var pie = d3.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.area; });
|
||||
|
||||
var path = d3.arc()
|
||||
.outerRadius(radius - 10)
|
||||
.innerRadius(0);
|
||||
|
||||
var label = d3.arc()
|
||||
.outerRadius(radius - 40)
|
||||
.innerRadius(radius - 40);
|
||||
|
||||
|
||||
d3.csv("{% url "get_csv" %}", function(d) {
|
||||
d.area = +d.area;
|
||||
return d;
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
var arc = g.selectAll(".arc")
|
||||
.data(pie(data))
|
||||
.enter().append("g")
|
||||
.attr("class", "arc");
|
||||
|
||||
arc.append("path")
|
||||
.attr("d", path)
|
||||
.attr("fill", function(d) { return color(d.data.name); });
|
||||
|
||||
arc.append("text")
|
||||
.attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
|
||||
.attr("dy", "0.35em")
|
||||
.text(function(d) { return d.data.name; });
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Pie Chart v2</title>
|
||||
<h1>Simple Pie Chart v2</h1>
|
||||
<h2>Zero values excluded</h2>
|
||||
<p>D3js version 4, use svg</p>
|
||||
<style>
|
||||
|
||||
.arc text {
|
||||
font: 10px sans-serif;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
<svg width="960" height="500"></svg>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
var svg = d3.select("svg"),
|
||||
width = +svg.attr("width"),
|
||||
height = +svg.attr("height"),
|
||||
radius = Math.min(width, height) / 2,
|
||||
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
var color = d3.scaleOrdinal(d3.schemeCategory20);
|
||||
|
||||
var pie = d3.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.{{ quantity }}; });
|
||||
|
||||
var path = d3.arc()
|
||||
.outerRadius(radius - 10)
|
||||
.innerRadius(0);
|
||||
|
||||
var label = d3.arc()
|
||||
.outerRadius(radius - 40)
|
||||
.innerRadius(radius - 40);
|
||||
|
||||
|
||||
d3.csv("/table?lyrname={{ lyrname }}&category={{ category }}&quantity={{ quantity }}", function(d) {
|
||||
d.area = +d.area;
|
||||
if (d.area !== 0) {
|
||||
return d;}
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
var arc = g.selectAll(".arc")
|
||||
.data(pie(data))
|
||||
.enter().append("g")
|
||||
.attr("class", "arc");
|
||||
|
||||
arc.append("path")
|
||||
.attr("d", path)
|
||||
.attr("fill", function(d) { return color(d.data.{{ category }}); });
|
||||
|
||||
arc.append("text")
|
||||
.attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
|
||||
.attr("dy", "0.35em")
|
||||
.text(function(d) { return d.data.{{ category }}; });
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,11 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^(?P<pk>[0-9]+)/$', views.ChartDetail.as_view(), name='chart_detail'),
|
||||
url(r'^add/(?P<layer_id>[0-9]+)/$', views.ChartCreate.as_view(), name='chart_add'),
|
||||
url(r'^update/(?P<pk>[0-9]+)/$', views.ChartUpdate.as_view(), name='chart_update'),
|
||||
url(r'^delete/(?P<pk>[0-9]+)/$', views.ChartDelete.as_view(), name='chart_delete'),
|
||||
url(r'^$', views.ChartList.as_view(), name='chart_list')
|
||||
]
|
||||
@@ -0,0 +1,106 @@
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||
from django.views.generic.list import ListView
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
|
||||
from geonode.layers.models import Layer
|
||||
|
||||
from .models import Chart, ChartForm
|
||||
from wfs_harvest.utils import get_fields
|
||||
|
||||
|
||||
class LoginRequiredMixin(object):
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
|
||||
return login_required(view)
|
||||
|
||||
|
||||
class ChartDetail(DetailView):
|
||||
model = Chart
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ChartDetail, self).get_object(queryset=queryset)
|
||||
lyr_obj = Layer.objects.get(pk=obj.layer)
|
||||
if not self.request.user.has_perm('download_resourcebase', lyr_obj.get_self_resource()):
|
||||
raise PermissionDenied
|
||||
return obj
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ChartDetail, self).get_context_data(**kwargs)
|
||||
return context
|
||||
|
||||
|
||||
class ChartCreate(LoginRequiredMixin, CreateView):
|
||||
form_class = ChartForm
|
||||
template_name_suffix = 'charts_app/chart_update_form.html'
|
||||
template_name = 'charts_app/chart_update_form.html'
|
||||
|
||||
def get_initial(self):
|
||||
layer_id = self.kwargs['layer_id']
|
||||
initial = self.initial.copy()
|
||||
initial['layer'] = layer_id
|
||||
return initial
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
layer_id = self.kwargs['layer_id']
|
||||
layer = Layer.objects.get(pk=layer_id)
|
||||
if not self.request.user.has_perm('download_resourcebase', layer.get_self_resource()):
|
||||
raise PermissionDenied
|
||||
fieldnames, num_fieldnames = get_fields(layer_id)
|
||||
ctx = super(ChartCreate, self).get_context_data(**kwargs)
|
||||
ctx['fieldnames'] = fieldnames
|
||||
ctx['num_fieldnames'] = num_fieldnames
|
||||
ctx['layer'] = layer
|
||||
return ctx
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.has_perm('download_resourcebase', form.instance.layer.get_self_resource()):
|
||||
raise PermissionDenied
|
||||
form.instance.created_by = self.request.user
|
||||
return super(ChartCreate, self).form_valid(form)
|
||||
|
||||
|
||||
class ChartUpdate(LoginRequiredMixin, UpdateView):
|
||||
model = Chart
|
||||
fields = '__all__'
|
||||
template_name = 'charts_app/chart_update_form.html'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ChartUpdate, self).get_object(queryset=queryset)
|
||||
is_chart_owner = (self.request.user == obj.created_by)
|
||||
lyr_obj = Layer.objects.get(pk=obj.layer)
|
||||
is_lyr_owner = (self.request.user == lyr_obj.owner)
|
||||
if not (self.request.user.is_superuser or is_chart_owner or is_lyr_owner):
|
||||
raise PermissionDenied
|
||||
return obj
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.created_by = self.request.user
|
||||
return super(ChartUpdate, self).form_valid(form)
|
||||
|
||||
|
||||
class ChartDelete(LoginRequiredMixin, DeleteView):
|
||||
model = Chart
|
||||
success_url = '/layers/'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ChartDelete, self).get_object(queryset=queryset)
|
||||
is_chart_owner = (self.request.user == obj.created_by)
|
||||
lyr_obj = Layer.objects.get(pk=obj.layer)
|
||||
is_lyr_owner = (self.request.user == lyr_obj.owner)
|
||||
if not (self.request.user.is_superuser or is_chart_owner or is_lyr_owner):
|
||||
raise PermissionDenied
|
||||
return obj
|
||||
|
||||
|
||||
class ChartList(ListView):
|
||||
model = Chart
|
||||
fields = '__all__'
|
||||
template_name = 'charts_app/chart_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ChartList, self).get_context_data(**kwargs)
|
||||
return context
|
||||
File renamed without changes.
@@ -0,0 +1,8 @@
|
||||
This directory is used to store static assets for your project. User media files
|
||||
(FileFields/ImageFields) are not stored here.
|
||||
|
||||
The convention for this directory is:
|
||||
|
||||
* css/ — stores CSS files
|
||||
* js/ — stores Javascript files
|
||||
* img/ — stores image files
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Whitespace-only changes.
Loaded 100 of 596 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user