├── .dockerignore
├── .gitignore
├── .pyup.yml
├── .travis.yml
├── CHANGES.rst
├── Dockerfile
├── LICENSE
├── README.rst
├── VERSION
├── app.json
├── branding
├── Lato.zip
├── Wazi logo reference.pdf
├── Wazi logo vector.ai
├── wazimap-sticker-final.pdf
├── wazimap-sticker-final.png
└── wazimap-sticker-final.svg
├── census
├── __init__.py
├── admin.py
├── context_processors.py
├── fixtures
│ └── summary_levels.json
├── management
│ ├── __init__.py
│ └── commands
│ │ ├── __init__.py
│ │ ├── cache_to_s3.py
│ │ └── taxonify_table_metadata.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_python3_port_20190315_0944.py
│ └── __init__.py
├── models.py
├── profile.py
├── static
│ ├── css
│ │ ├── _empty.scss
│ │ └── vendor
│ │ │ ├── Grid.css
│ │ │ ├── images
│ │ │ ├── layers-2x.png
│ │ │ ├── layers.png
│ │ │ ├── marker-icon-2x.png
│ │ │ ├── marker-icon.png
│ │ │ └── marker-shadow.png
│ │ │ ├── leaflet-0.6.4.css
│ │ │ ├── leaflet-0.6.4.ie.css
│ │ │ ├── leaflet.label.css
│ │ │ ├── normalize.css
│ │ │ ├── normalize.min.css
│ │ │ └── select2
│ │ │ ├── select2.css
│ │ │ ├── select2.png
│ │ │ ├── select2.textinput.css
│ │ │ ├── select2x2.png
│ │ │ └── spinner.gif
│ ├── embed.html
│ ├── iframe.html
│ ├── img
│ │ ├── code4sa-logo-small.png
│ │ ├── examples
│ │ │ ├── education_cleveland.png
│ │ │ ├── hover_over_graph.png
│ │ │ ├── language_los_angeles.png
│ │ │ ├── map_search.png
│ │ │ ├── poverty_spokane.png
│ │ │ ├── section_overview.png
│ │ │ ├── text_search.png
│ │ │ ├── us_geographic_mobility_distribution.png
│ │ │ ├── us_median_income_map.png
│ │ │ └── us_race_table.png
│ │ ├── icons
│ │ │ ├── favicon.ico
│ │ │ └── touch-icon-144x144.png
│ │ ├── logo-48x24.png
│ │ ├── logo-embed.png
│ │ ├── logo.png
│ │ ├── logo12.png
│ │ ├── mma.png
│ │ ├── openup-logo-small.png
│ │ ├── questions
│ │ │ ├── age-sex.png
│ │ │ ├── ancestry.png
│ │ │ ├── citizenship.png
│ │ │ ├── commuting.png
│ │ │ ├── disability.png
│ │ │ ├── education.png
│ │ │ ├── employment.png
│ │ │ ├── fertility.png
│ │ │ ├── health-insurance.png
│ │ │ ├── housing-fuel.png
│ │ │ ├── income.png
│ │ │ ├── industry.png
│ │ │ ├── language.png
│ │ │ ├── marriage.png
│ │ │ ├── migration.png
│ │ │ ├── mortgage.png
│ │ │ ├── occupation.png
│ │ │ ├── place-of-birth.png
│ │ │ ├── property-value.png
│ │ │ ├── public-assistance.png
│ │ │ ├── race.png
│ │ │ ├── relationship.png
│ │ │ ├── same-sex.png
│ │ │ ├── seniors.png
│ │ │ ├── tenure.png
│ │ │ ├── va_rating.png
│ │ │ ├── veteran.png
│ │ │ └── veteran_period.png
│ │ └── wazi-logo.png
│ ├── js
│ │ ├── address.search.js
│ │ ├── app.js
│ │ ├── charts.js
│ │ ├── comparisons.js
│ │ ├── cr-leaflet.js
│ │ ├── data.query.builder.js
│ │ ├── embed.chart.frame.js
│ │ ├── embed.chart.make.js
│ │ ├── glossary.js
│ │ ├── head2head.js
│ │ ├── profile.topic.picker.js
│ │ ├── table.detail.js
│ │ ├── tilelayer.js
│ │ ├── vendor
│ │ │ ├── Grid.js
│ │ │ ├── chroma.js
│ │ │ ├── chroma.min.js
│ │ │ ├── classList.js
│ │ │ ├── classList.min.js
│ │ │ ├── flippant.js
│ │ │ ├── flippant.min.js
│ │ │ ├── leaflet-0.6.4.js
│ │ │ ├── leaflet.label.js
│ │ │ ├── r2d3.js
│ │ │ ├── select2.js
│ │ │ ├── select2.min.js
│ │ │ └── topojson.v1.min.js
│ │ └── widget.geo.select.js
│ └── tilelayer.html
├── templates
│ ├── 404.html
│ ├── 500.html
│ ├── _base.html
│ ├── about.html
│ ├── data
│ │ ├── _base_data.html
│ │ ├── _blocks
│ │ │ ├── _data_query_builder.html
│ │ │ ├── _toggle_menu.html
│ │ │ └── _topic_select_input.html
│ │ ├── data_builder.html
│ │ ├── data_distribution.html
│ │ ├── data_map.html
│ │ └── data_table.html
│ ├── examples
│ │ ├── _base_examples.html
│ │ └── embed_charts.html
│ ├── glossary.html
│ ├── healthcheck.html
│ ├── homepage.html
│ ├── locate
│ │ └── locate.html
│ ├── profile
│ │ ├── _base_profile.html
│ │ ├── _blocks
│ │ │ ├── _comparative_list_item.html
│ │ │ ├── _header_extra_links.html
│ │ │ └── _stat_list.html
│ │ ├── profile_detail.html
│ │ └── profile_search.html
│ ├── search
│ │ ├── elasticsearch.html
│ │ ├── geo_search.html
│ │ └── table_search.html
│ ├── table
│ │ ├── _base_table.html
│ │ ├── specific
│ │ │ ├── B02003.html
│ │ │ └── _base_specific.html
│ │ ├── table_detail.html
│ │ └── table_search.html
│ └── topics
│ │ ├── _base_topics.html
│ │ ├── _question_aside.html
│ │ ├── about_census.html
│ │ ├── age_sex.html
│ │ ├── children.html
│ │ ├── commute.html
│ │ ├── employment.html
│ │ ├── families.html
│ │ ├── geography.html
│ │ ├── getting_started.html
│ │ ├── health-insurance.html
│ │ ├── income.html
│ │ ├── migration.html
│ │ ├── poverty.html
│ │ ├── public-assistance.html
│ │ ├── race_hispanic.html
│ │ ├── same-sex.html
│ │ ├── seniors.html
│ │ ├── table-codes.html
│ │ ├── topics_list.html
│ │ └── veterans.html
├── templatetags
│ ├── __init__.py
│ ├── comparatives.py
│ ├── lookup.py
│ ├── madlibs.py
│ ├── partition.py
│ ├── sumlevs.py
│ └── tabletags.py
├── tests.py
├── topics.py
├── urls.py
├── utils.py
└── views.py
├── compose.yaml
├── deploy
├── .buildpacks
├── Procfile
└── runtime.txt
├── docs
├── Makefile
├── conf.py
├── config.rst
├── customising.rst
├── data.rst
├── deploying.rst
├── geos.rst
├── index.rst
├── make.bat
├── profiles.rst
├── started.rst
├── upgrading.rst
└── version_history.rst
├── manage.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── wazimap
├── __init__.py
├── admin.py
├── apps.py
├── context_processors.py
├── data
├── __init__.py
├── base.py
├── download.py
├── tables.py
└── utils.py
├── geo.py
├── management
├── __init__.py
└── commands
│ ├── __init__.py
│ └── upgradetables.py
├── middleware.py
├── migrations
├── 0001_initial.py
├── 0002_geography_long_name.py
├── 0003_remove_geography_osm_area_id.py
├── 0004_auto_20160302_1645.py
├── 0005_unique-geo-add-year.py
├── 0006_geo-year-to-geo-version.py
├── 0007_fieldtable_geoversion.py
├── 0008_auto_20170424_1209.py
├── 0009_datatables.py
├── 0010_null-total-column.py
├── 0011_release_citation.py
├── 0012_auto_20190111_1342.py
├── 0013_port_to_python3.py
├── 0014_auto_20191021_1216.py
└── __init__.py
├── models
├── __init__.py
├── data.py
└── geo.py
├── profiles.py
├── settings.py
├── static
├── css
│ ├── _app.scss
│ ├── _censusreporter.scss
│ ├── _charts.scss
│ ├── _custom.scss
│ ├── _variables.scss
│ ├── embed.scss
│ ├── vendor
│ │ └── _normalize.min.scss
│ └── wazimap.scss
└── js
│ ├── comparisons.js
│ ├── data.query.builder.js
│ ├── embed.chart.frame.js
│ ├── maps_static.js
│ ├── profile_map.js
│ ├── table.detail.js
│ └── widget.geo.select.js
├── templates
├── _base.html
├── _footer.html
├── data
│ ├── _base_data.html
│ ├── _blocks
│ │ └── _data_query_builder.html
│ └── data_map.html
├── embed
│ └── iframe.html
├── examples
│ └── embed_charts.html
├── help.html
├── homepage.html
├── locate
│ └── locate.html
├── profile
│ ├── _blocks
│ │ ├── _comparative_list_item.html
│ │ └── _stat_list.html
│ ├── head2head.html
│ └── profile_detail.html
├── settings_js.html
└── table
│ └── table_detail.html
├── templatetags
├── __init__.py
├── jsonify.py
└── stats.py
├── tests
├── __init__.py
├── data
│ ├── __init__.py
│ └── test_utils.py
├── support.py
└── test_geo.py
├── urls.py
├── views.py
└── wsgi.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Include any files or directories that you don't want to be copied to your
2 | # container here (e.g., local build artifacts, temporary files, etc.).
3 | #
4 | # For more help, visit the .dockerignore file reference guide at
5 | # https://docs.docker.com/engine/reference/builder/#dockerignore-file
6 |
7 | **/.DS_Store
8 | **/__pycache__
9 | **/.venv
10 | **/.classpath
11 | **/.dockerignore
12 | **/.env
13 | **/.git
14 | **/.gitignore
15 | **/.project
16 | **/.settings
17 | **/.toolstarget
18 | **/.vs
19 | **/.vscode
20 | **/*.*proj.user
21 | **/*.dbmdl
22 | **/*.jfm
23 | **/bin
24 | **/charts
25 | **/docker-compose*
26 | **/compose*
27 | **/Dockerfile*
28 | **/node_modules
29 | **/npm-debug.log
30 | **/obj
31 | **/secrets.dev.yaml
32 | **/values.dev.yaml
33 | LICENSE
34 | README.md
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | env/
3 | /dist/
4 | _build/
5 | *.egg-info/
6 | *.pyc
7 | *.csv
8 | node_modules/
9 | censusreporter/config/dev/local.py
10 | .idea/
11 | censusreporter/embed_data/profiles/*
12 | debug
13 | .vagrant
14 | .eggs/
15 |
--------------------------------------------------------------------------------
/.pyup.yml:
--------------------------------------------------------------------------------
1 | # See https://pyup.io/docs/bot/config/
2 | update: insecure
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: xenial
2 | language: python
3 | python:
4 | - "3.7"
5 | services:
6 | - postgresql
7 | env:
8 | - DATABASE_URL=postgres://postgres:@localhost:5432/wazimap
9 | before_install:
10 | - sudo rm -f /etc/boto.cfg # workaround for https://github.com/travis-ci/travis-ci/issues/7940
11 | install:
12 | - python setup.py -q install
13 | script: python manage.py test
14 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.7.17-slim-bullseye
2 |
3 | ENV PYTHONUNBUFFERED=1 \
4 | PIP_NO_CACHE_DIR=1 \
5 | PIP_DISABLE_PIP_VERSION_CHECK=1
6 |
7 | RUN set -ex \
8 | && apt-get update \
9 | && apt-get upgrade -y \
10 | && apt-get install -y build-essential \
11 | && apt-get install -y libpq-dev \
12 | && apt-get install -y git \
13 | && apt-get install -y libgdal-dev \
14 | && apt-get autoremove -y \
15 | && apt-get clean -y \
16 | && rm -rf /var/lib/apt/lists/*
17 |
18 | WORKDIR /app
19 |
20 | COPY . .
21 |
22 | RUN pip install --upgrade pip \
23 | pip install .
24 |
25 | EXPOSE 5000
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Census Reporter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 | The Wazimap name and branding is Copyright 2013-2017 Media Monitoring Africa (MMA)
24 | and may not be used without permission.
25 |
26 | If you use this software, please provide attribution to Census Reporter,
27 | Wazimap, Media Monitoring Africa and OpenUp.
28 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Wazimap
2 | =======
3 |
4 | .. image:: https://badge.fury.io/py/wazimap.svg
5 | :target: http://badge.fury.io/py/wazimap
6 |
7 | .. image:: https://travis-ci.org/Code4SA/wazimap.svg
8 | :target: http://travis-ci.org/Code4SA/wazimap
9 |
10 | This is the latest version of wazimap. Version 1 is located in the releases-v1 branch.
11 |
12 |
13 | Wazimap is a Django application for exploring census and other similar data. It makes it easy to understand a place
14 | through the eyes of the data, and to explore data across a range of places. It is most suited for census data
15 | but can easily be used with other data that is similarly focused on places in a country.
16 |
17 | Check out `Wazimap South Africa `_ and `HURUmap `_ to
18 | get an idea of what Wazimap is about.
19 |
20 | Wazimap is a fork of the excellent `Censusreporter `_ project which was funded by a
21 | `Knight News Challenge grant `_.
22 | You can also find `Censusreporter on GitHub `_.
23 |
24 | Wazimap builds on Censusreporter and makes it easier to re-use. Wazimap was originally built by
25 | `OpenUp `_ with the support of `Media Monitoring Africa `_.
26 | It is maintained by OpenUp.
27 |
28 | * Wazimap is on Twitter as `@Wazimap `_.
29 | * Read the `full Wazimap documentation `_.
30 |
31 | Using Wazimap
32 | =============
33 |
34 | Read the `full Wazimap documentation `_ to get started.
35 |
36 | Development
37 | ===========
38 |
39 | Releasing a New Version
40 | -----------------------
41 |
42 | 1. Run the tests::
43 |
44 | python manage.py test
45 |
46 | 2. Update VERSION appropriately
47 | 3. Update the CHANGES.rst
48 | 4. Commit and push to github
49 | 5. Release to PyPI::
50 |
51 | python setup.py sdist bdist_wheel upload
52 |
53 | License and Copyright
54 | =====================
55 |
56 | Copyright (c) 2014 Census Reporter.
57 |
58 | Wazimap is licensed under the MIT License.
59 |
60 | The Wazimap name and branding is Copyright 2013-2017 Media Monitoring Africa (MMA) and may not be used without permission.
61 |
62 | If you use this software, please provide attribution to Census Reporter, Wazimap, Media Monitoring Africa and OpenUp.
63 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 2.1.3
2 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dokku": {
4 | "predeploy": "python manage.py compilescss && python manage.py collectstatic --noinput && rm -rf /var/tmp/wazimap_cache"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/branding/Lato.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/branding/Lato.zip
--------------------------------------------------------------------------------
/branding/Wazi logo reference.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/branding/Wazi logo reference.pdf
--------------------------------------------------------------------------------
/branding/Wazi logo vector.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/branding/Wazi logo vector.ai
--------------------------------------------------------------------------------
/branding/wazimap-sticker-final.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/branding/wazimap-sticker-final.pdf
--------------------------------------------------------------------------------
/branding/wazimap-sticker-final.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/branding/wazimap-sticker-final.png
--------------------------------------------------------------------------------
/census/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/__init__.py
--------------------------------------------------------------------------------
/census/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import SubjectConcept, SummaryLevel, Geography
4 |
5 | class SubjectConceptAdmin(admin.ModelAdmin):
6 | save_on_top = True
7 | prepopulated_fields = {'slug': ('name',)}
8 | list_display = ('name', 'census_category')
9 | list_editable = ('census_category',)
10 |
11 | class SummaryLevelAdmin(admin.ModelAdmin):
12 | save_on_top = True
13 | prepopulated_fields = {'slug': ('name',)}
14 | list_display = ('summary_level', 'name', 'short_name','plural_name')
15 | list_editable = ('short_name', 'plural_name')
16 | filter_horizontal = ('ancestors',)
17 | list_display_links = ('name',)
18 |
19 | admin.site.register(SubjectConcept, SubjectConceptAdmin)
20 | admin.site.register(SummaryLevel, SummaryLevelAdmin)
21 | admin.site.register(Geography)
22 |
--------------------------------------------------------------------------------
/census/context_processors.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 | def api_url(request):
4 | return {'API_URL': settings.API_URL}
5 |
--------------------------------------------------------------------------------
/census/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/management/__init__.py
--------------------------------------------------------------------------------
/census/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/management/commands/__init__.py
--------------------------------------------------------------------------------
/census/management/commands/cache_to_s3.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from django.core.management.base import BaseCommand
3 | from multiprocessing import Pool
4 | from traceback import format_exc
5 | from boto.s3.connection import S3Connection
6 | from boto.s3.key import Key
7 |
8 | import json
9 | import io
10 | import gzip
11 |
12 | from ...profile import geo_profile, enhance_api_data
13 |
14 | import logging
15 | logging.basicConfig(level=logging.WARN)
16 | logger = logging.getLogger(__name__)
17 |
18 | s3 = S3Connection()
19 |
20 | def s3_keyname(geoid):
21 | return '/1.0/data/profiles/%s.json' % geoid
22 |
23 | def key(geoid):
24 | bucket = s3.get_bucket('embed.censusreporter.org')
25 | keyname = s3_keyname(geoid)
26 | key = Key(bucket, keyname)
27 |
28 | return key
29 |
30 | def write_profile_json(s3_key, data):
31 | s3_key.metadata['Content-Type'] = 'application/json'
32 | s3_key.metadata['Content-Encoding'] = 'gzip'
33 | s3_key.storage_class = 'REDUCED_REDUNDANCY'
34 |
35 | # create gzipped version of json in memory
36 | memfile = io.StringIO()
37 | #memfile.write(data)
38 | with gzip.GzipFile(filename=s3_key.key, mode='wb', fileobj=memfile) as gzip_data:
39 | gzip_data.write(data)
40 | memfile.seek(0)
41 |
42 | # store static version on S3
43 | s3_key.set_contents_from_file(memfile)
44 |
45 | def seed(geoid):
46 | logger.info("Working on {}".format(geoid))
47 | try:
48 | api_data = geo_profile(geoid)
49 | api_data = enhance_api_data(api_data)
50 |
51 | s3key = key(geoid)
52 | write_profile_json(s3key, json.dumps(api_data))
53 | logger.info("Wrote to key {}".format(s3key))
54 | except Exception as e:
55 | logger.error("Problem caching {}".format(geoid))
56 | logger.exception(e)
57 | logger.info("Done working on {}".format(geoid))
58 |
59 |
60 | class Command(BaseCommand):
61 | help = 'Pre-generates some Census Reporter content and places it on S3.'
62 |
63 | def handle(self, *args, **options):
64 | if not args:
65 | print("Please include the name of a file containing the seed geo_ids.")
66 | return False
67 |
68 | parallelism = 4
69 | if 'parallelism' in options:
70 | parallelism = int(options.get('parallelism'))
71 |
72 | pool = Pool(parallelism)
73 |
74 | seed_file = open(args[0], 'r')
75 |
76 | for geoid in seed_file:
77 | pool.apply_async(seed, (geoid.strip(),))
78 |
79 | pool.close()
80 | pool.join()
81 |
--------------------------------------------------------------------------------
/census/management/commands/taxonify_table_metadata.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from django.core.management.base import BaseCommand
3 | from census.models import Table
4 |
5 | SUBJECT_AREA_TO_TOPICS = {
6 | 'Age-Sex': 'age, gender',
7 | 'Hispanic Origin': 'race',
8 | 'Race': 'race',
9 |
10 | 'Earnings': 'income',
11 | 'Employment Status': 'employment',
12 | 'Health Insurance': 'health insurance',
13 | 'Income': 'income',
14 | 'Industry-Occupation-Class of Worker': 'employment',
15 | 'Journey to Work': 'employment, commute',
16 | 'Poverty': 'poverty',
17 | 'Transfer Programs': 'public assistance',
18 |
19 | 'Ancestry': 'ancestry',
20 | 'Children - Relationship': 'children',
21 | 'Disability': 'disability',
22 | 'Educational Attainment': 'education',
23 | 'Fertility': 'fertility',
24 | 'Foreign Birth': 'place of birth',
25 | 'Grand(Persons) - Age of HH Members': 'children, grandparents',
26 | 'Households - Families': 'families',
27 | 'Language': 'language',
28 | 'Marital Status': 'marital status',
29 | 'Place of Birth - Native': 'place of birth',
30 | 'Residence Last Year - Migration': 'migration',
31 | 'School Enrollment': 'education',
32 | 'Veteran Status': 'veterans',
33 |
34 | 'Housing': 'housing',
35 | }
36 |
37 | TABLE_NAME_TEXT_TO_TOPICS = {
38 | 'children': 'children',
39 | 'disability': 'disability',
40 | 'bachelor\'s degree': 'education',
41 | 'education': 'education',
42 | 'school': 'education',
43 | 'employ': 'employment',
44 | 'occupation': 'employment',
45 | 'work': 'employment',
46 | 'families': 'families',
47 | 'family': 'families',
48 | 'nonfamily': 'roommates',
49 | 'grandparent': 'grandparents',
50 | 'health insurance': 'health insurance',
51 | 'living arrange': 'housing',
52 | #'household': 'households',
53 | 'earnings': 'income',
54 | 'income': 'income',
55 | 'geographical mobility': 'migration',
56 | 'poverty': 'poverty',
57 | 'food stamps': 'public assistance',
58 | 'public assistance': 'public assistance',
59 | '65 years and over': 'seniors',
60 | 'transportation': 'commute',
61 | 'va health care': 'veterans',
62 | 'veteran': 'veterans',
63 | }
64 |
65 | TABLE_NAME_TEXT_TO_FACETS = {
66 | 'by age': 'age',
67 | 'age by': 'age',
68 | 'citizenship': 'citizenship',
69 | 'naturalization': 'citizenship',
70 | 'by famil': 'families',
71 | 'by sex': 'gender',
72 | 'sex by': 'gender',
73 | #'by household': 'household type',
74 | #'household type by': 'household type',
75 | 'language': 'language',
76 | 'marriage': 'marital status',
77 | 'marital': 'marital status',
78 | 'nativity': 'place of birth',
79 | 'place of birth': 'place of birth',
80 | #'by relationship': 'relationship type',
81 | '(white': 'race',
82 | '(black': 'race',
83 | '(american': 'race',
84 | '(asian': 'race',
85 | '(native': 'race',
86 | '(some other race': 'race',
87 | '(two or more races': 'race',
88 | 'hispanic': 'race',
89 | }
90 |
91 | class Command(BaseCommand):
92 | help = 'Uses dicts above to apply taxonomy to Census table names.'
93 | def handle(self, *args, **options):
94 | table_list = Table.objects.all()
95 |
96 | for table in table_list:
97 | table_name = table.table_name
98 | subject_area = table.subject_area
99 | topics = []
100 | print(table_name)
101 |
102 | if subject_area in SUBJECT_AREA_TO_TOPICS:
103 | # only keep the rows in the subject areas we want
104 | topics.extend(
105 | [topic.strip() for topic in SUBJECT_AREA_TO_TOPICS[subject_area].split(',')]
106 | )
107 |
108 | for key in TABLE_NAME_TEXT_TO_TOPICS:
109 | if key in table_name.lower():
110 | topics.extend(
111 | [topic.strip() for topic in TABLE_NAME_TEXT_TO_TOPICS[key].split(',')]
112 | )
113 |
114 | # for now, we're storing these in the same place
115 | for key in TABLE_NAME_TEXT_TO_FACETS:
116 | if key in table_name.lower():
117 | topics.extend(
118 | [facet.strip() for facet in TABLE_NAME_TEXT_TO_FACETS[key].split(',')]
119 | )
120 |
121 | topics = ', '.join(sorted(set(topics)))
122 | table.topics = topics
123 | table.save()
124 |
--------------------------------------------------------------------------------
/census/migrations/0002_python3_port_20190315_0944.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.20 on 2019-03-15 07:44
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('census', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='subjectconcept',
17 | name='census_category',
18 | field=models.CharField(blank=True, default='Population', max_length=128),
19 | ),
20 | migrations.AlterField(
21 | model_name='subjectconcept',
22 | name='source',
23 | field=models.CharField(blank=True, default='American Community Survey Subject Definitions', max_length=64),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/census/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/migrations/__init__.py
--------------------------------------------------------------------------------
/census/static/css/_empty.scss:
--------------------------------------------------------------------------------
1 | // exists to force scss to use this as an include directory
2 |
--------------------------------------------------------------------------------
/census/static/css/vendor/images/layers-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/images/layers-2x.png
--------------------------------------------------------------------------------
/census/static/css/vendor/images/layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/images/layers.png
--------------------------------------------------------------------------------
/census/static/css/vendor/images/marker-icon-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/images/marker-icon-2x.png
--------------------------------------------------------------------------------
/census/static/css/vendor/images/marker-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/images/marker-icon.png
--------------------------------------------------------------------------------
/census/static/css/vendor/images/marker-shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/images/marker-shadow.png
--------------------------------------------------------------------------------
/census/static/css/vendor/leaflet-0.6.4.ie.css:
--------------------------------------------------------------------------------
1 | .leaflet-vml-shape {
2 | width: 1px;
3 | height: 1px;
4 | }
5 | .lvml {
6 | behavior: url(#default#VML);
7 | display: inline-block;
8 | position: absolute;
9 | }
10 |
11 | .leaflet-control {
12 | display: inline;
13 | }
14 |
15 | .leaflet-popup-tip {
16 | width: 21px;
17 | _width: 27px;
18 | margin: 0 auto;
19 | _margin-top: -3px;
20 |
21 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
22 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
23 | }
24 | .leaflet-popup-tip-container {
25 | margin-top: -1px;
26 | }
27 | .leaflet-popup-content-wrapper, .leaflet-popup-tip {
28 | border: 1px solid #999;
29 | }
30 | .leaflet-popup-content-wrapper {
31 | zoom: 1;
32 | }
33 |
34 | .leaflet-control-zoom,
35 | .leaflet-control-layers {
36 | border: 3px solid #999;
37 | }
38 | .leaflet-control-layers-toggle {
39 | }
40 | .leaflet-control-attribution,
41 | .leaflet-control-layers,
42 | .leaflet-control-scale-line {
43 | background: white;
44 | }
45 | .leaflet-zoom-box {
46 | filter: alpha(opacity=50);
47 | }
48 | .leaflet-control-attribution {
49 | border-top: 1px solid #bbb;
50 | border-left: 1px solid #bbb;
51 | }
52 |
--------------------------------------------------------------------------------
/census/static/css/vendor/leaflet.label.css:
--------------------------------------------------------------------------------
1 | .leaflet-label {
2 | background: #fff;
3 | background-clip: padding-box;
4 | border: solid 4px #111;
5 | -webkit-border-radius: 4px;
6 | -moz-border-radius: 4px;
7 | border-radius: 4px;
8 | color: #111;
9 | display: block;
10 | position: absolute;
11 | -webkit-user-select: none;
12 | -moz-user-select: none;
13 | -ms-user-select: none;
14 | user-select: none;
15 | pointer-events: none;
16 | white-space: nowrap;
17 | z-index: 6;
18 | font-size: 12px;
19 | line-height: 1.4em;
20 | font-weight: bold;
21 | padding: 1px 6px;
22 | }
23 |
24 | .leaflet-label.leaflet-clickable {
25 | cursor: pointer;
26 | }
27 |
28 | .leaflet-label:before,
29 | .leaflet-label:after {
30 | border-top: 6px solid transparent;
31 | border-bottom: 6px solid transparent;
32 | content: none;
33 | position: absolute;
34 | top: 5px;
35 | }
36 |
37 | .leaflet-label:before {
38 | border-right: 6px solid black;
39 | border-right-color: inherit;
40 | left: -10px;
41 | }
42 |
43 | .leaflet-label:after {
44 | border-left: 6px solid black;
45 | border-left-color: inherit;
46 | right: -10px;
47 | }
48 |
49 | .leaflet-label-right:before,
50 | .leaflet-label-left:after {
51 | content: "";
52 | }
--------------------------------------------------------------------------------
/census/static/css/vendor/normalize.min.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */
2 | article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:1em 40px}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}dl,menu,ol,ul{margin:1em 0}dd{margin:0 0 0 40px}menu,ol,ul{padding:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0;white-space:normal;*margin-left:-7px}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}
--------------------------------------------------------------------------------
/census/static/css/vendor/select2/select2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/select2/select2.png
--------------------------------------------------------------------------------
/census/static/css/vendor/select2/select2x2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/select2/select2x2.png
--------------------------------------------------------------------------------
/census/static/css/vendor/select2/spinner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/css/vendor/select2/spinner.gif
--------------------------------------------------------------------------------
/census/static/embed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Embed test
5 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/census/static/iframe.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
18 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/census/static/img/code4sa-logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/code4sa-logo-small.png
--------------------------------------------------------------------------------
/census/static/img/examples/education_cleveland.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/education_cleveland.png
--------------------------------------------------------------------------------
/census/static/img/examples/hover_over_graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/hover_over_graph.png
--------------------------------------------------------------------------------
/census/static/img/examples/language_los_angeles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/language_los_angeles.png
--------------------------------------------------------------------------------
/census/static/img/examples/map_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/map_search.png
--------------------------------------------------------------------------------
/census/static/img/examples/poverty_spokane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/poverty_spokane.png
--------------------------------------------------------------------------------
/census/static/img/examples/section_overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/section_overview.png
--------------------------------------------------------------------------------
/census/static/img/examples/text_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/text_search.png
--------------------------------------------------------------------------------
/census/static/img/examples/us_geographic_mobility_distribution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/us_geographic_mobility_distribution.png
--------------------------------------------------------------------------------
/census/static/img/examples/us_median_income_map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/us_median_income_map.png
--------------------------------------------------------------------------------
/census/static/img/examples/us_race_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/examples/us_race_table.png
--------------------------------------------------------------------------------
/census/static/img/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/icons/favicon.ico
--------------------------------------------------------------------------------
/census/static/img/icons/touch-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/icons/touch-icon-144x144.png
--------------------------------------------------------------------------------
/census/static/img/logo-48x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/logo-48x24.png
--------------------------------------------------------------------------------
/census/static/img/logo-embed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/logo-embed.png
--------------------------------------------------------------------------------
/census/static/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/logo.png
--------------------------------------------------------------------------------
/census/static/img/logo12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/logo12.png
--------------------------------------------------------------------------------
/census/static/img/mma.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/mma.png
--------------------------------------------------------------------------------
/census/static/img/openup-logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/openup-logo-small.png
--------------------------------------------------------------------------------
/census/static/img/questions/age-sex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/age-sex.png
--------------------------------------------------------------------------------
/census/static/img/questions/ancestry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/ancestry.png
--------------------------------------------------------------------------------
/census/static/img/questions/citizenship.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/citizenship.png
--------------------------------------------------------------------------------
/census/static/img/questions/commuting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/commuting.png
--------------------------------------------------------------------------------
/census/static/img/questions/disability.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/disability.png
--------------------------------------------------------------------------------
/census/static/img/questions/education.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/education.png
--------------------------------------------------------------------------------
/census/static/img/questions/employment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/employment.png
--------------------------------------------------------------------------------
/census/static/img/questions/fertility.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/fertility.png
--------------------------------------------------------------------------------
/census/static/img/questions/health-insurance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/health-insurance.png
--------------------------------------------------------------------------------
/census/static/img/questions/housing-fuel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/housing-fuel.png
--------------------------------------------------------------------------------
/census/static/img/questions/income.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/income.png
--------------------------------------------------------------------------------
/census/static/img/questions/industry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/industry.png
--------------------------------------------------------------------------------
/census/static/img/questions/language.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/language.png
--------------------------------------------------------------------------------
/census/static/img/questions/marriage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/marriage.png
--------------------------------------------------------------------------------
/census/static/img/questions/migration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/migration.png
--------------------------------------------------------------------------------
/census/static/img/questions/mortgage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/mortgage.png
--------------------------------------------------------------------------------
/census/static/img/questions/occupation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/occupation.png
--------------------------------------------------------------------------------
/census/static/img/questions/place-of-birth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/place-of-birth.png
--------------------------------------------------------------------------------
/census/static/img/questions/property-value.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/property-value.png
--------------------------------------------------------------------------------
/census/static/img/questions/public-assistance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/public-assistance.png
--------------------------------------------------------------------------------
/census/static/img/questions/race.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/race.png
--------------------------------------------------------------------------------
/census/static/img/questions/relationship.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/relationship.png
--------------------------------------------------------------------------------
/census/static/img/questions/same-sex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/same-sex.png
--------------------------------------------------------------------------------
/census/static/img/questions/seniors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/seniors.png
--------------------------------------------------------------------------------
/census/static/img/questions/tenure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/tenure.png
--------------------------------------------------------------------------------
/census/static/img/questions/va_rating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/va_rating.png
--------------------------------------------------------------------------------
/census/static/img/questions/veteran.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/veteran.png
--------------------------------------------------------------------------------
/census/static/img/questions/veteran_period.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/questions/veteran_period.png
--------------------------------------------------------------------------------
/census/static/img/wazi-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/static/img/wazi-logo.png
--------------------------------------------------------------------------------
/census/static/js/embed.chart.make.js:
--------------------------------------------------------------------------------
1 | function makeCensusEmbeds() {
2 | var embed = {
3 | embeds: {}
4 | };
5 |
6 | embed.init = function() {
7 | embed.containers = document.querySelectorAll('.census-reporter-embed');
8 | embed.numContainers = embed.containers.length;
9 | for (var i = 0; i < embed.numContainers; i++) {
10 | embed.embeds[embed.containers[i].id] = {
11 | naturalWidth: embed.containers[i].width,
12 | naturalHeight: embed.containers[i].height,
13 | frameHeight: embed.containers[i].height
14 | }
15 | }
16 | embed.addListeners();
17 | embed.sendDataToFrames({ resize: 'resize' });
18 | }
19 |
20 | embed.addListeners = function() {
21 | var eventMethod = window.addEventListener ? 'addEventListener' : 'attachEvent',
22 | eventListener = window[eventMethod],
23 | messageEvent = (eventMethod == 'attachEvent') ? 'onmessage' : 'message',
24 | resizeEvent = (eventMethod == 'attachEvent') ? 'onresize' : 'resize';
25 |
26 | eventListener(messageEvent, embed.handleMessage, false);
27 | eventListener(resizeEvent, embed.resize);
28 | }
29 |
30 | embed.handleMessage = function(event) {
31 | var messageData = JSON.parse(event.data);
32 | if (messageData.chartHeight && messageData.containerID) {
33 | embed.embeds[messageData.containerID].frameHeight = messageData.chartHeight;
34 | embed.setFrameSizes();
35 | }
36 | }
37 |
38 | embed.debounce = function(func, wait, immediate) {
39 | var timeout;
40 | return function() {
41 | var context = this, args = arguments;
42 | var later = function() {
43 | timeout = null;
44 | if (!immediate) func.apply(context, args);
45 | };
46 | var callNow = immediate && !timeout;
47 | clearTimeout(timeout);
48 | timeout = setTimeout(later, wait);
49 | if (callNow) func.apply(context, args);
50 | };
51 | };
52 |
53 | embed.resize = embed.debounce(function() {
54 | embed.setFrameSizes();
55 | embed.sendDataToFrames({ resize: 'resize' });
56 | }, 100);
57 |
58 | embed.setFrameSizes = function() {
59 | for (var i = 0; i < embed.numContainers; i++) {
60 | var thisContainer = embed.containers[i],
61 | thisEmbed = embed.embeds[embed.containers[i].id],
62 | parentWidth = thisContainer.offsetWidth;
63 | thisContainer.width = (parentWidth <= thisEmbed.naturalWidth) ? parentWidth : thisEmbed.naturalWidth;
64 | thisContainer.height = ((thisEmbed.frameHeight + 80) >= thisEmbed.naturalHeight) ? +thisEmbed.frameHeight+80 : thisEmbed.naturalHeight;
65 | }
66 | }
67 |
68 | embed.sendDataToFrames = function(data) {
69 | // IE9 can only send strings
70 | for (var i = 0; i < embed.numContainers; i++) {
71 | embed.containers[i].contentWindow.postMessage(JSON.stringify(data), '*');
72 | }
73 | }
74 |
75 | embed.init();
76 |
77 | return embed;
78 | }
79 |
80 | window.onload = function() {
81 | window.CensusReporterEmbeds = makeCensusEmbeds();
82 | };
83 |
--------------------------------------------------------------------------------
/census/static/js/head2head.js:
--------------------------------------------------------------------------------
1 | /* Helper routines for managing the head-to-head comparison view.
2 | *
3 | * They ensure that the two iframes are sized to match the height
4 | * of their content, to prevent scrolling.
5 | */
6 | function Head2Head() {
7 | var self = this;
8 |
9 | self.initParent = function() {
10 | // this is the parent frame in the head-to-head view
11 | self.isParent = true;
12 | self.isChild = false;
13 | };
14 |
15 | self.initChild = function() {
16 | // this is a child frame in the head-to-head view
17 | self.isParent = false;
18 | self.isChild = true;
19 |
20 | $('body').addClass('profile-head2head-frame');
21 | $('body').on('click', 'a[href]', self.navigateTo);
22 |
23 | // set the frame height
24 | setTimeout(self.resizeChild, 500);
25 | $(window).on('resize', _.debounce(self.resizeChild, 500));
26 | };
27 |
28 | self.resizeChild = function(e) {
29 | // set the iframe to fit the size of the child
30 | var height = document.body.offsetHeight + 50,
31 | frame = $(window.frameElement);
32 |
33 | if (frame.height() != height) {
34 | // height changed, update the iframe and check again in a few msecs
35 | frame.height(height);
36 | setTimeout(self.resizeChild, 500);
37 | }
38 | };
39 |
40 | self.navigateTo = function(e) {
41 | // open links in the parent window
42 | if (e.target.href) {
43 | e.preventDefault();
44 | window.parent.location = e.target.href;
45 | }
46 | };
47 | }
48 |
49 | var h2h = new Head2Head();
50 |
51 | if (window.parent != window && window.parent.location.pathname.indexOf('/compare/') > -1) {
52 | h2h.initChild();
53 | } else if (window.location.pathname.indexOf('/compare/') > -1) {
54 | h2h.initParent();
55 | }
56 |
--------------------------------------------------------------------------------
/census/static/js/profile.topic.picker.js:
--------------------------------------------------------------------------------
1 | // powers the topic picker on profile pages
2 |
3 | // the template including this should set the following vars:
4 | // var thisGeoID = '{{ geography.this.full_geoid }}',
5 | // placeGeoID = '{{ geography.parents.place.full_geoid }}',
6 | // CBSAGeoID = '{{ geography.parents.CBSA.full_geoid }}',
7 | // countyGeoID = '{{ geography.county.full_geoid }}',
8 | // stateGeoID = '{{ geography.state.full_geoid }}',
9 | // nationGeoID = '{{ geography.nation.full_geoid }}';
10 |
11 | var theseGeoIDs = [thisGeoID, placeGeoID, CBSAGeoID, countyGeoID, stateGeoID, nationGeoID].filter(function(n){return n}),
12 | chosenTableID = chosenTableID || null,
13 | topicSelect = $('#topic-select');
14 |
15 | var tableSearchAPI = CR_API_URL + '/1.0/table/search',
16 | rootGeoAPI = CR_API_URL + '/1.0/geo/tiger2013/',
17 | dataAPI = CR_API_URL + '/1.0/data/show/latest';
18 |
19 | var topicSelectEngine = new Bloodhound({
20 | datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.full_name); },
21 | queryTokenizer: Bloodhound.tokenizers.whitespace,
22 | limit: 1500,
23 | remote: {
24 | url: tableSearchAPI,
25 | replace: function (url, query) {
26 | return url += '?q=' + query;
27 | },
28 | filter: function(response) {
29 | var resultNumber = response.length;
30 | if (resultNumber === 0) {
31 | response.push({
32 | table_name: 'Sorry, no matches found. Try changing your search.'
33 | });
34 | }
35 | _.map(response, function(item) {
36 | if (!!item['topics']) {
37 | item['topic_string'] = item['topics'].join(', ');
38 | }
39 | });
40 | return response;
41 | }
42 | }
43 | });
44 | topicSelectEngine.initialize();
45 |
46 | function makeTopicSelectWidget(element) {
47 | element.typeahead('destroy');
48 | element.typeahead({
49 | autoselect: true,
50 | highlight: false,
51 | hint: false,
52 | minLength: 2
53 | }, {
54 | name: 'topics',
55 | displayKey: 'simple_table_name',
56 | source: topicSelectEngine.ttAdapter(),
57 | templates: {
58 | suggestion: Handlebars.compile(
59 | [
60 | '{{#if table_id}}{{#if column_name}}Column in {{/if}}Table {{table_id}} {{/if}}',
61 | '{{simple_table_name}}
',
62 | '{{#if column_name}}Column name: {{column_name}}
{{/if}}',
63 | '{{#if topic_string}}Table topics: {{topic_string}}
{{/if}}'
64 | ].join('')
65 | )
66 | }
67 | });
68 |
69 | element.on('typeahead:selected', function(obj, datum) {
70 | chosenTableID = datum['table_id'];
71 | if (!!chosenTableID) {
72 | window.location = '/data/table/?table=' + chosenTableID + "&geo_ids=" + thisGeoID + "&primary_geo_id=" + thisGeoID;
73 | }
74 | });
75 | }
76 |
77 | jQuery(document).ready(function(){
78 | // initial setup for select widget
79 | makeTopicSelectWidget(topicSelect);
80 | });
81 |
--------------------------------------------------------------------------------
/census/static/js/vendor/classList.js:
--------------------------------------------------------------------------------
1 | /*
2 | * classList.js: Cross-browser full element.classList implementation.
3 | * 2014-01-31
4 | *
5 | * By Eli Grey, http://eligrey.com
6 | * Public Domain.
7 | * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 | */
9 |
10 | /*global self, document, DOMException */
11 |
12 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
13 |
14 | if ("document" in self && !("classList" in document.createElement("_"))) {
15 |
16 | (function (view) {
17 |
18 | "use strict";
19 |
20 | if (!('Element' in view)) return;
21 |
22 | var
23 | classListProp = "classList"
24 | , protoProp = "prototype"
25 | , elemCtrProto = view.Element[protoProp]
26 | , objCtr = Object
27 | , strTrim = String[protoProp].trim || function () {
28 | return this.replace(/^\s+|\s+$/g, "");
29 | }
30 | , arrIndexOf = Array[protoProp].indexOf || function (item) {
31 | var
32 | i = 0
33 | , len = this.length
34 | ;
35 | for (; i < len; i++) {
36 | if (i in this && this[i] === item) {
37 | return i;
38 | }
39 | }
40 | return -1;
41 | }
42 | // Vendors: please allow content code to instantiate DOMExceptions
43 | , DOMEx = function (type, message) {
44 | this.name = type;
45 | this.code = DOMException[type];
46 | this.message = message;
47 | }
48 | , checkTokenAndGetIndex = function (classList, token) {
49 | if (token === "") {
50 | throw new DOMEx(
51 | "SYNTAX_ERR"
52 | , "An invalid or illegal string was specified"
53 | );
54 | }
55 | if (/\s/.test(token)) {
56 | throw new DOMEx(
57 | "INVALID_CHARACTER_ERR"
58 | , "String contains an invalid character"
59 | );
60 | }
61 | return arrIndexOf.call(classList, token);
62 | }
63 | , ClassList = function (elem) {
64 | var
65 | trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
66 | , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
67 | , i = 0
68 | , len = classes.length
69 | ;
70 | for (; i < len; i++) {
71 | this.push(classes[i]);
72 | }
73 | this._updateClassName = function () {
74 | elem.setAttribute("class", this.toString());
75 | };
76 | }
77 | , classListProto = ClassList[protoProp] = []
78 | , classListGetter = function () {
79 | return new ClassList(this);
80 | }
81 | ;
82 | // Most DOMException implementations don't allow calling DOMException's toString()
83 | // on non-DOMExceptions. Error's toString() is sufficient here.
84 | DOMEx[protoProp] = Error[protoProp];
85 | classListProto.item = function (i) {
86 | return this[i] || null;
87 | };
88 | classListProto.contains = function (token) {
89 | token += "";
90 | return checkTokenAndGetIndex(this, token) !== -1;
91 | };
92 | classListProto.add = function () {
93 | var
94 | tokens = arguments
95 | , i = 0
96 | , l = tokens.length
97 | , token
98 | , updated = false
99 | ;
100 | do {
101 | token = tokens[i] + "";
102 | if (checkTokenAndGetIndex(this, token) === -1) {
103 | this.push(token);
104 | updated = true;
105 | }
106 | }
107 | while (++i < l);
108 |
109 | if (updated) {
110 | this._updateClassName();
111 | }
112 | };
113 | classListProto.remove = function () {
114 | var
115 | tokens = arguments
116 | , i = 0
117 | , l = tokens.length
118 | , token
119 | , updated = false
120 | ;
121 | do {
122 | token = tokens[i] + "";
123 | var index = checkTokenAndGetIndex(this, token);
124 | if (index !== -1) {
125 | this.splice(index, 1);
126 | updated = true;
127 | }
128 | }
129 | while (++i < l);
130 |
131 | if (updated) {
132 | this._updateClassName();
133 | }
134 | };
135 | classListProto.toggle = function (token, force) {
136 | token += "";
137 |
138 | var
139 | result = this.contains(token)
140 | , method = result ?
141 | force !== true && "remove"
142 | :
143 | force !== false && "add"
144 | ;
145 |
146 | if (method) {
147 | this[method](token);
148 | }
149 |
150 | return !result;
151 | };
152 | classListProto.toString = function () {
153 | return this.join(" ");
154 | };
155 |
156 | if (objCtr.defineProperty) {
157 | var classListPropDesc = {
158 | get: classListGetter
159 | , enumerable: true
160 | , configurable: true
161 | };
162 | try {
163 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
164 | } catch (ex) { // IE 8 doesn't support enumerable:true
165 | if (ex.number === -0x7FF5EC54) {
166 | classListPropDesc.enumerable = false;
167 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
168 | }
169 | }
170 | } else if (objCtr[protoProp].__defineGetter__) {
171 | elemCtrProto.__defineGetter__(classListProp, classListGetter);
172 | }
173 |
174 | }(self));
175 |
176 | }
177 |
--------------------------------------------------------------------------------
/census/static/js/vendor/classList.min.js:
--------------------------------------------------------------------------------
1 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
2 | ;if("document" in self&&!("classList" in document.createElement("_"))){(function(j){"use strict";if(!("Element" in j)){return}var a="classList",f="prototype",m=j.Element[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p{{geo_level}} {{full_name}}
'),
3 | geoSelect = $('#geography-select, #geography-select-home');
4 |
5 | var textMatchEngine = new Bloodhound({
6 | datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.full_name); },
7 | queryTokenizer: Bloodhound.tokenizers.whitespace,
8 | limit: 20,
9 | remote: {
10 | url: textmatchAPI,
11 | rateLimitWait: 600,
12 | replace: function (url, query) {
13 | return url += '?q=' + query;
14 | },
15 | filter: function(response) {
16 | return response.results;
17 | }
18 | }
19 | });
20 | textMatchEngine.initialize();
21 |
22 | function makeGeoSelectWidget(element, selected) {
23 | element.typeahead({
24 | autoselect: true,
25 | highlight: false,
26 | hint: false,
27 | minLength: 2
28 | }, {
29 | // get textual matches from host
30 | name: 'textmatch',
31 | displayKey: 'full_name',
32 | source: textMatchEngine.ttAdapter(),
33 | limit: 20,
34 | templates: {
35 | suggestion: resultTemplate,
36 | },
37 | });
38 |
39 | element.on('typeahead:selected', selected || function(event, datum) {
40 | event.stopPropagation();
41 | window.location = '/profiles/' + datum.full_geoid + '/';
42 | });
43 | }
44 |
45 | makeGeoSelectWidget(geoSelect);
46 | makeGeoSelectWidget($('#compare-place-select'), function(event, datum) {
47 | var geoId = [profileData.geography.this.geo_level, profileData.geography.this.geo_code].join('-');
48 | event.stopPropagation();
49 | window.location = '/compare/' + geoId + '/vs/' + datum.full_geoid + '/';
50 | });
51 |
--------------------------------------------------------------------------------
/census/static/tilelayer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Example of using Census Reporter GeoJSON tiles on your own maps.
5 |
6 |
7 |
8 | -- pick a summary level --
9 |
10 |
11 |
12 |
13 |
14 |
107 |
108 |
--------------------------------------------------------------------------------
/census/templates/404.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}{% load humanize %}
2 |
3 | {% block head_title %}{{ block.super }}{% endblock %}
4 |
5 | {% block content %}
6 |
7 |
8 | Not Found
9 | {% for message in messages %}
10 | {{ message|safe }}
11 | {% endfor %}
12 |
13 |
14 | In the spirit of open and iterative development, we’re making this site available to the public as early as possible. Much will change, and some things may not be working at any moment, but we want people to see what we’re working on and we welcome input about what might be most useful. (See below for all manner of contact information.)
15 |
16 | {% endblock %}
--------------------------------------------------------------------------------
/census/templates/500.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}{% load humanize %}
2 |
3 | {% block head_title %}{{ block.super }}{% endblock %}
4 |
5 | {% block content %}
6 |
7 |
10 |
11 | Uh-oh, something’s gone wrong. We’ve logged the error and will check it out as soon as possible. In the meantime, do you want to return to the homepage ?
12 |
13 | {% endblock %}
--------------------------------------------------------------------------------
/census/templates/data/_base_data.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
3 | {% comment %}
4 | This page provides a base for all data view templates to inherit from,
5 | for applying things across that entire family of pages.
6 | {% endcomment %}
7 |
8 | {% block head_title %}Table {% if table %}{{ table }}{% endif %} {% if release %}({{ release.name }}){% endif %} - {{ block.super }}{% endblock %}
9 |
10 | {% block body_class %}data-view{% endblock %}
11 |
12 | {% block header_content %}
13 |
16 | {% endblock %}
17 |
18 | {% block content %}
19 | {% include 'data/_blocks/_topic_select_input.html' %}
20 |
21 |
22 |
23 | Table explorer
24 | Change table
25 |
26 |
27 |
28 |
29 | Table {{ table }}
30 |
35 | {% block aside_notes %}{% endblock %}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {% endblock %}
45 |
46 | {% block body_javascript_extra %}
47 |
48 | {% block page_specific_javascript %}{% endblock %}
49 |
75 | {% endblock %}
76 |
--------------------------------------------------------------------------------
/census/templates/data/_blocks/_data_query_builder.html:
--------------------------------------------------------------------------------
1 |
9 |
16 |
23 |
--------------------------------------------------------------------------------
/census/templates/data/_blocks/_toggle_menu.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/census/templates/data/_blocks/_topic_select_input.html:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/census/templates/data/data_builder.html:
--------------------------------------------------------------------------------
1 | {% extends 'data/_base_data.html' %}{% load humanize %}
2 |
3 | {% block body_class %}data-builder{% endblock %}
4 |
5 | {% block header_content %}{% endblock %}
6 |
7 | {% block content %}
8 |
9 | {% include "data/_blocks/_data_query_builder.html" %}
10 |
11 | {% endblock %}
12 |
13 | {% block body_javascript_extra %}
14 |
15 | {% endblock %}
--------------------------------------------------------------------------------
/census/templates/data/data_distribution.html:
--------------------------------------------------------------------------------
1 | {% extends 'data/_base_data.html' %}{% load humanize %}
2 |
3 | {% block head_title %}Distribution View: {{ block.super }}{% endblock %}
4 |
5 | {% block head_css_extra %}
6 |
7 | {% endblock %}
8 |
9 | {% block aside_notes %}
10 | Charts show how data in this table is distributed, helping identify patterns, clusters and outliers. You can click a point to lock and unlock display.
11 | {% endblock %}
12 |
13 | {% block page_specific_javascript %}
14 |
15 |
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/census/templates/data/data_map.html:
--------------------------------------------------------------------------------
1 | {% extends 'data/_base_data.html' %}{% load humanize %}
2 | {% load staticfiles %}
3 |
4 | {% block head_title %}Map View: {{ block.super }}{% endblock %}
5 |
6 | {% block head_css_extra %}
7 |
8 |
11 |
12 | {% endblock %}
13 |
14 | {% block body_class %}{{ block.super }} full-screen{% endblock %}
15 |
16 | {% block body %}
17 |
18 |
23 |
24 |
25 | {% include 'data/_blocks/_toggle_menu.html' %}
26 | {% include 'data/_blocks/_topic_select_input.html' %}
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 | {% endblock %}
38 |
39 | {% block page_specific_javascript %}
40 |
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/census/templates/data/data_table.html:
--------------------------------------------------------------------------------
1 | {% extends 'data/_base_data.html' %}{% load humanize %}
2 |
3 | {% block head_title %}Grid View: {{ block.super }}{% endblock %}
4 |
5 | {% block head_css_extra %}
6 |
7 | {% endblock %}
8 |
9 | {% block aside_notes %}
10 | Add data for more places below; visualize or download this data with controls at right.
11 | {% endblock %}
12 |
13 | {% block page_specific_javascript %}
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/census/templates/examples/_base_examples.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
--------------------------------------------------------------------------------
/census/templates/healthcheck.html:
--------------------------------------------------------------------------------
1 | OK
--------------------------------------------------------------------------------
/census/templates/locate/locate.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
3 |
4 | {% block content %}
5 |
6 |
19 | {% if places %}
20 |
32 | {% endif %}
33 |
34 | {% endblock %}
35 |
36 | {% block body_javascript_extra %}
37 |
38 |
62 | {% endblock %}
63 |
--------------------------------------------------------------------------------
/census/templates/profile/_base_profile.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
3 | {% block head_title %}Profile data - {{ block.super }}{% endblock %}
4 |
5 | {% comment %}
6 | At some point, we may end up needing to branch the profile page template
7 | in order to support data available for different releases. And we may end up
8 | with a special template for subtopic pages within a profile. This page provides
9 | a profile-specific base suitable for applying things across that entire
10 | family of pages.
11 | {% endcomment %}
12 |
13 |
--------------------------------------------------------------------------------
/census/templates/profile/_blocks/_comparative_list_item.html:
--------------------------------------------------------------------------------
1 | {% load humanize madlibs %}
2 | {# to be called via `comparatives` inclusion tag #}
3 |
4 | {% if index and stat_type != 'count' %}
5 | {{ index|comparison_index_phrase }} the {{ stat_type|stat_type_to_number_noun }} in {{ place_name }}: {% if stat_type == 'dollar' %}R{% endif %}{{ value|floatformat|intcomma|cut:".0" }}{% if stat_type == 'percentage' %}%{% endif %}
6 | {% if error_ratio >= 10 %}† {% endif %}
7 | {% else %}
8 | {{ place_name }}: {% if stat_type == 'dollar' %}R{% endif %}{{ value|floatformat|intcomma|cut:".0" }}{% if stat_type == 'percentage' %}%{% endif %}
9 | {% endif %}
10 | {% if numerator %}
11 | {% if stat_type == 'dollar' %}R{% endif %}{{ numerator|floatformat|intcomma|cut:".0" }}
12 | {% if error %}
13 | (±{% if stat_type == 'dollar' %}R{% endif %}{{ error|floatformat|intcomma|cut:".0" }}{% if stat_type == 'percentage' %}%{% endif %}{% if numerator_error %} / ±{% if stat_type == 'dollar' %}R{% endif %}{{ numerator_error|floatformat|intcomma|cut:".0" }}{% endif %})
14 | {% endif %}
15 | {% elif error %}
16 | ±{% if stat_type == 'dollar' %}R{% endif %}{{ error|floatformat|intcomma|cut:".0" }}{% if stat_type == 'percentage' %}%{% endif %}
17 | {% endif %}
18 |
19 |
--------------------------------------------------------------------------------
/census/templates/profile/_blocks/_header_extra_links.html:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/census/templates/profile/_blocks/_stat_list.html:
--------------------------------------------------------------------------------
1 | {% load humanize madlibs comparatives %}
2 | {% if not stat_wrapper == 'false' %}{% endif %}
3 |
4 |
5 | {% if not stat.values.this %}
6 | {{ WAZIMAP.na_label }}
7 | {% else %}
8 | {% if stat_type == 'name' %}
9 | {{ stat.name }}
10 | {% else %}
11 | {% if stat_type == 'dollar' %}R{% endif %}{{ stat.values.this|floatformat|intcomma|cut:".0" }}{% if stat_type == 'percentage' %}%{% endif %}{% if stat_suffix %} {{ stat_suffix }} {% endif %}
12 | {% endif %}
13 | {% if stat.error.this_ratio >= 10 %}† {% endif %}
14 | {% endif %}
15 |
16 | {% if not stat_class == 'secondary' %}
17 | {% if stat.error.this %}
18 | ±{% if stat_type == 'dollar' %}R{% endif %}{{ stat.error.this|floatformat|intcomma|cut:".0" }}{% if stat_type == 'percentage' %}%{% endif %}
19 | {% endif %}
20 |
21 | {% if stat.numerators.this %}
22 | ({% if stat_type == 'dollar' %}R{% endif %}{{ stat.numerators.this|floatformat|intcomma|cut:".0" }} ±{% if stat_type == 'dollar' %}R{% endif %}{{ stat.numerator_errors.this|floatformat|intcomma|cut:".0" }})
23 | {% endif %}
24 | {% endif %}
25 |
26 | {% if stat_type == 'name' %}{{ stat_name }}{% else %}{{ stat.name }}{% endif %}
27 |
28 | {% if stat_class == 'secondary' %}
29 |
30 | {% if stat.error.this %}
31 | ±{% if stat_type == 'dollar' %}R{% endif %}{{ stat.error.this|floatformat|intcomma|cut:".0" }}{% if stat_type == 'percentage' %}%{% endif %}
32 | {% endif %}
33 |
34 | {% if stat.numerators.this %}
35 | ({% if stat_type == 'dollar' %}R{% endif %}{{ stat.numerators.this|floatformat|intcomma|cut:".0" }} ±{% if stat_type == 'dollar' %}R{% endif %}{{ stat.numerator_errors.this|floatformat|intcomma|cut:".0" }})
36 | {% endif %}
37 |
38 | {% endif %}
39 |
40 |
41 | {% if geography.comparatives and stat.values.this and not stat_class == 'secondary' %}
42 |
43 | {% for sumlev in geography.comparatives %}
44 | {% build_comparative_item sumlev stat stat_type geography %}
45 | {% endfor %}
46 |
47 | {% endif %}
48 | {% if not stat_wrapper == 'false' %} {% endif %}
49 |
--------------------------------------------------------------------------------
/census/templates/search/geo_search.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 | Census Reporter Geo Search Example
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/census/templates/search/table_search.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
3 | {% block head_css_extra %}
4 |
20 | {% endblock %}
21 |
22 | {% block content %}
23 |
24 | Search data tables and columns
25 |
36 |
37 |
38 | {% if tables %}
39 |
40 | Tables
41 |
42 |
43 |
44 | Table ID
45 | Table Name
46 | Universe
47 |
48 |
49 |
50 | {% for table in tables %}
51 |
52 | {{ table.release }} {{ table.table_id }}
53 | {{ table.table_name }}
54 | {{ table.table_universe }}
55 |
56 | {% endfor %}
57 |
58 |
59 |
60 | {% endif %}
61 |
62 | {% if columns %}
63 |
64 | Columns
65 |
66 |
67 |
68 | Table ID
69 | Table Name
70 | Column ID
71 | Column Name
72 |
73 |
74 |
75 | {% for column in columns %}
76 |
77 | {{ column.table.release }} {{ column.parent_table_id }}
78 | {{ column.table.table_name }}
79 | {{ column.column_id }}
80 | {{ column.column_name }}
81 |
82 | {% endfor %}
83 |
84 |
85 |
86 | {% endif %}
87 |
88 | {% endblock %}
89 |
--------------------------------------------------------------------------------
/census/templates/table/_base_table.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
--------------------------------------------------------------------------------
/census/templates/table/specific/B02003.html:
--------------------------------------------------------------------------------
1 | {% extends "table/specific/_base_specific.html" %}
2 | {% block table_specific %}
3 |
4 | Because the columns in B02003 are so detailed, this table is only available for the entire US, and is only included in the 1-year and 3-year ACS releases. If you want similar information for any other geographies or for the 5-year ACS, use table C02003
5 |
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/census/templates/table/specific/_base_specific.html:
--------------------------------------------------------------------------------
1 |
2 |
About this table
3 | {% block table_specific %}
4 |
5 | Extend this template to maintain consistency.
6 | Fill in this block with details specific to a table.
7 | Don't forget to use the 'explain' class on your <p> tags.
8 |
9 | {% endblock %}
10 |
11 |
--------------------------------------------------------------------------------
/census/templates/topics/_base_topics.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
3 | {% block head_title %}{% if topic.title %}{{ topic.title }}{% else %}Topics{% endif %} - {{ block.super }}{% endblock %}
4 |
5 | {% block content_container %}
6 |
7 |
In addition to serving data, Census Reporter wants to help you understand how to use it. Let us know if there are topics you’d like to see us explain.
8 |
9 |
10 |
11 | {% if topic.slug %}← Topics
{% endif %}
12 | {{ topic.title }}
13 | {{ topic.description }}
14 |
15 |
16 | {% block content %}{% endblock %}
17 |
18 |
19 | {% endblock %}
20 |
21 | {% block body_javascript_extra %}{{ block.super }}
22 |
42 | {% endblock %}
--------------------------------------------------------------------------------
/census/templates/topics/_question_aside.html:
--------------------------------------------------------------------------------
1 | {% if topic.question_images or topic.question_pdfs %}
2 |
3 | {% if topic.question_images %}
4 | Sample survey questions on this topic
5 | {% for image in topic.question_images %}
6 |
7 | {% endfor %}
8 | {% endif %}
9 |
10 | {% if topic.question_pdfs %}
11 | More information from the Census (PDFs)
12 |
13 | {% for label,link in topic.question_pdfs %}
14 | {{ label }}
15 | {% endfor %}
16 |
17 | {% endif %}
18 |
19 | {% endif %}
20 |
--------------------------------------------------------------------------------
/census/templates/topics/about_census.html:
--------------------------------------------------------------------------------
1 | {% extends 'topics/_base_topics.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "topics/_question_aside.html" %}
6 | The High Level
7 | Originally, the intention of the US Census was to count the population every ten years so that representation could be apportioned. Over time, more questions were added to the decennial census, and other completely new data collection projects were developed.
8 | Today, the Census Bureau has four major programs: the decennial census , the Current Population Survey (CPS) , the Population Estimates Program, and the American Community Survey (ACS) . Census Reporter (this site) only offers data from the ACS.
9 | These programs differ in their data collection methodology, the frequency with which data is released, and the level of geographic specificity they can provide. There are a few other Census products
10 |
11 |
12 | The Decennial Census
13 | As its name indicates, the decennial census is run every ten years. It attempts to actually count every resident in the United States as of a specific day (April 1st of the census year, in recent decades.) Because it is a count and not a survey , the Census can publish precise counts for much smaller geographies than the other products.
14 |
15 | {% endblock %}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/census/templates/topics/age_sex.html:
--------------------------------------------------------------------------------
1 | {% extends 'topics/_base_topics.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "topics/_question_aside.html" %}
6 | Overview
7 | Throughout the American Community Survey, data is broken down by age and sex. However, there are a few tables that are focused directly on these basic statistics.
8 |
9 | Note: do not substitute the word “gender” for “sex.” Census Bureau specifically refers to the biological sex of survey respondents, not their gender presentation.
10 |
11 |
12 | B01001 : Sex by Age: provides the estimated count of men and women in age buckets, mostly of five years (e.g. 25 to 29 years, 30 to 34 years). Around the later teens and in the 60’s the brackets are of differing sizes, including a column for just 20-year-olds and another for just 21-year-olds.)
13 | B01002 : Median Age: provides the estimated median age for all people in the given geography, and then also for males and females separately.
14 | B01003 : Total population: a single column with the estimated total population for the geography. This value is the same as the first column of B01001 for a given geography.
15 |
16 |
17 |
18 |
19 |
20 | Code
21 | Title
22 |
23 |
24 |
25 |
26 | B01001 † ‡
27 | Sex by Age
28 |
29 |
30 | B01002 †
31 | Median Age by Sex
32 |
33 |
34 | B01003
35 | Total Population
36 |
37 |
38 |
39 |
40 |
41 | † Also available in racial iterations .
42 | ‡ Table also available in “collapsed” version: change “B” to “C” for table code. Note: There is no table C01001 .
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | Age and Sex elsewhere in the American Community Survey
52 |
53 | Hundreds of ACS tables are tabulated by age and sex. So it’s not worth listing them all here. If you are looking for a table and it has what you want, but is also split by age and sex, you may have some of your own work to do. These tables always have the total in the first column, so you may just want that and disregard the tabulations. There are also usually columns for all male and all female, but if you want to count by age and not by sex, you will have to sum those values.
54 |
55 | {% endblock %}
56 |
57 |
58 |
--------------------------------------------------------------------------------
/census/templates/topics/getting_started.html:
--------------------------------------------------------------------------------
1 | {% extends 'topics/_base_topics.html' %}
2 |
3 | {% block content %}
4 |
5 | What's in the Census?
6 | The Census isn't one thing. The U.S. Census Bureau has a number of data collection programs and even more data products based on them. Census Reporter focuses on just one of them, the American Community Survey, or ACS. Our About the Census page gets into the basics you should know about the other Census products.
7 | OK, then, what's in the ACS?
8 | The ACS covers a good variety of general-interest topics about the US: not only basic demographics like age, sex, and race, but also a number of economic and social questions, and details about the actual homes where people live, like how many rooms are in a housing unit and how many units are in the building. Our guide to the structure of table codes includes a list of the main subject areas.
9 | When is the ACS updated?
10 |
11 | Every fall the Census Bureau releases new ACS estimates in three forms. The different releases are based on data collected over different periods of time. Collecting data over longer periods of time helps to reduce the margin of error for places with a smaller population.
12 |
13 |
14 | The releases are staggered, with the 1-year release delivered first, usually in September, followed by the 3-year release, and finally, the 5-year release is usually delivered in early December.
15 |
16 |
17 | Census Reporter always shows you the data for the release appropriate to the places you're looking at, so you generally don't have to worry about it too much. However, technically you shouldn't directly compare data from different releases, so be careful if you are gathering estimates from different profile pages.
18 |
19 |
20 |
21 |
22 | Collection Period
23 | Geographies covered
24 | Month released
25 |
26 |
27 |
28 |
29 | 1 year
30 | Population > 65,000
31 | September
32 |
33 |
34 | 3 years
35 | Population > 20,000
36 | October
37 |
38 |
39 | 5 years
40 | All major geographies down to block group
41 | December
42 |
43 |
44 |
45 |
46 | {% endblock %}
47 |
48 |
49 |
--------------------------------------------------------------------------------
/census/templates/topics/public-assistance.html:
--------------------------------------------------------------------------------
1 | {% extends 'topics/_base_topics.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "topics/_question_aside.html" %}
6 | Overview
7 | These tables address the topic of public assistance.
8 |
9 | The ACS tables beginning with 22 are about public assistance. In the listing below, table names have been simplified in some cases. References to children and grandchildren almost always mean under 18 years old. Income is for the last 12 months, adjusted for inflation to the value of dollars in the last year of the survey period. References to poverty status/level, receipt of food stamps/SNAP, and work status are all for the last 12 months.
10 |
11 |
12 |
13 | Code
14 | Title
15 |
16 |
17 |
18 | B22001 ‡
19 | Receipt of Food Stamps/SNAP by Presence of People 60 Years and Over for Households
20 |
21 |
22 | B22002 ‡
23 | Receipt of Food Stamps/SNAP by Presence of Children by Household Type for Households
24 |
25 |
26 | B22003
27 | Receipt of Food Stamps/SNAP by Poverty Status for Households
28 |
29 |
30 | B22004
31 | Receipt of Food Stamps by Disability Status for Households
32 |
33 |
34 | B22005 †
35 | Receipt of Food Stamps/SNAP by Race of Householder
36 |
37 |
38 | B22007 ‡
39 | Receipt of Food Stamps/SNAP by Family Type by Number of Workers in Family
40 |
41 |
42 | B22008
43 | Median Household Income by Receipt of Food Stamps/SNAP
44 |
45 |
46 | B22010
47 | Receipt of Food Stamps/SNAP by Disability Status for Households
48 |
49 |
50 |
51 |
52 |
53 | ‡ Table also available in "collapsed" version: change "B" to "C" for table code.
54 | † Also available in racial iterations .
55 |
56 |
57 |
58 |
59 |
60 |
61 | Other ACS tables that relate to public assistance
62 |
63 |
64 |
65 | Code
66 | Title
67 |
68 |
69 |
70 | B09010
71 | Receipt of Supplemental Security Income (SSI), Cash Public Assistance Income, or Food Stamps/SNAP by Household Type for Children in Households
72 |
73 |
74 | B13015
75 | Women 15 to 50 Years Who Had a Birth in the Past 12 Months by Marital Status and Receipt of Public Assistance Income
76 |
77 |
78 | B17015 ‡
79 | Poverty Status of Families by Family Type by Social Security Income by Supplemental Security Income (SSI) and Cash Public Assistance Income
80 |
81 |
82 | B19057
83 | Public Assistance Income for Households
84 |
85 |
86 | B19058
87 | Public Assistance Income or Food Stamps/SNAP for Households
88 |
89 |
90 | B19067
91 | Aggregate Public Assistance Income for Households
92 |
93 |
94 |
95 |
96 |
97 | ‡ Table also available in "collapsed" version: change "B" to "C" for table code.
98 |
99 |
100 |
101 |
102 |
103 | {% endblock %}
104 |
105 |
106 |
--------------------------------------------------------------------------------
/census/templates/topics/same-sex.html:
--------------------------------------------------------------------------------
1 | {% extends 'topics/_base_topics.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "topics/_question_aside.html" %}
6 | Overview
7 | The Census Bureau does not ask questions about sexual orientation. However, responses to some questions provide data about same-sex couples. This data should not be used to draw general conclusions about gay/lesbian/bisexual Americans, but when handled with appropriate care, may still be useful for reporting.
8 |
9 | Table B11009 , Unmarried-partner Households by Sex of Partner , is the only table which provides information about people who have told the Census Bureau that they live with a partner of the same sex. As explained in the Census Bureau's Frequently Asked Questions About Same-Sex Couple Households document, responses reporting a spouse of the same sex as the house holder are changed to indicate an unmarried partner, regardless of state laws permitting same-sex marriage.
10 |
11 |
12 | Consult the Census Bureau's documentation for more details. Specifically, American Community Survey Data on Same Sex Couples provides national-level data on the household characteristics (including income, education, etc) of same-sex couples compared to married and unmarried opposite-sex couples and married couples dating back to 2005, as well as more data and methodological details.
13 |
14 |
15 | Census Bureau Information about Same-Sex Couples
16 |
24 |
25 |
26 | {% endblock %}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/census/templates/topics/topics_list.html:
--------------------------------------------------------------------------------
1 | {% extends 'topics/_base_topics.html' %}
2 |
3 | {% block content %}
4 |
5 |
6 | {% for topic in topics_list %}
7 | {{ topic.title }}
8 | {{ topic.description|safe|linebreaks }}
9 | {% endfor %}
10 |
11 |
12 | {% endblock %}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/census/templates/topics/veterans.html:
--------------------------------------------------------------------------------
1 | {% extends 'topics/_base_topics.html' %}
2 |
3 | {% block content %}
4 |
5 | {% include "topics/_question_aside.html" %}
6 | Overview
7 |
8 | The Census Bureau asks three questions specific to service in the armed forces. The tables summarizing responses to those questions are listed below. In addition to the Veteran-specific tables, there are a few other pertinent tables to know about.
9 |
10 | Employment
11 |
12 | In general, the Census tables about employment include "Armed Forces" as a component of the labor force. So, you can use tables like B23001 to know how many people are in active service for a given geography. (See the B23002 series for the same data broken down in racial iterations .) Table B23025 presents employment status by industry, including active service armed forces.
13 |
14 | Social and Family Characteristics
15 |
16 | These tables have "armed forces" broken out as a separate column:
17 |
18 | B12006 : Marital Status by Sex by Labor Force Participation
19 | B23003 : Presence of Own Children by Age of Children
20 | B23006 : Educational Attainment by Employment Status
21 | B23007 : Presence of Own Children by Family Type by Employment
22 | B23024 : Poverty Status by Disability Status by Employment Status
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Code
31 | title
32 |
33 |
34 |
35 |
36 | B21001 †
37 | Sex by Age by Veteran Status for the Civilian Population
38 |
39 | B21002
40 | Period of Military Service for Civilian Veterans
41 |
42 | B21003
43 | Veteran Status by Educational Attainment for the Civilian Population
44 |
45 | B21004
46 | Median Income by Veteran Status by Sex for the Civilian Population
47 |
48 |
49 | B21005 ‡
50 | Age by Veteran Status by Employment Status
51 |
52 | B21007 ‡
53 | Age by Veteran Status by Poverty Status in the Past 12 Months by Disability Status for the Civilian Population
54 |
55 | B21100 ‡
56 | Service-connected Disability-rating Status and Ratings for Civilian Veterans
57 |
58 | B99211
59 | Imputation of Veteran Status for the Population
60 |
61 | B99212
62 | Imputation of Period of Military Service for Civilian Veterans
63 |
64 |
65 |
66 |
67 |
68 | † Also available in racial iterations .
69 |
70 |
71 |
72 |
73 | ‡ Table also available in "collapsed" version: change "B" to "C" for table code.
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | {% endblock %}
--------------------------------------------------------------------------------
/census/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/census/templatetags/__init__.py
--------------------------------------------------------------------------------
/census/templatetags/comparatives.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 |
6 | @register.inclusion_tag('profile/_blocks/_comparative_list_item.html')
7 | def build_comparative_item(sumlev, stat, stat_type, geography, decimals=None):
8 | if sumlev == 'CBSA':
9 | place_name = 'the %s' % geography['parents'][sumlev]['full_name']
10 | else:
11 | place_name = geography['parents'][sumlev]['short_name']
12 |
13 | item_context = {
14 | 'place_name': place_name,
15 | 'stat_type': stat_type,
16 | 'decimals': decimals,
17 | 'value': stat['values'][sumlev],
18 | 'index': stat['index'][sumlev],
19 | 'error': stat['error'][sumlev],
20 | 'error_ratio': stat['error_ratio'][sumlev],
21 | 'numerator': stat['numerators'][sumlev],
22 | 'numerator_error': stat['numerator_errors'][sumlev],
23 | }
24 | return item_context
25 |
--------------------------------------------------------------------------------
/census/templatetags/lookup.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 | @register.filter
6 | def get(d, key):
7 | return d.get(key, '')
8 |
--------------------------------------------------------------------------------
/census/templatetags/madlibs.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.utils.safestring import mark_safe
3 |
4 | register = template.Library()
5 |
6 | COMPARISON_PHRASE_MAP = {
7 | 206: ["more than double", ""],
8 | 195: ["about double", ""],
9 | 180: ["nearly double", ""],
10 | 161: ["more than 1.5 times", ""],
11 | 145: ["about 1.5 times", ""],
12 | 135: ["about 1.4 times", ""],
13 | 128: ["about 1.3 times", ""],
14 | 122: ["about 25 percent higher", "than"],
15 | 115: ["about 20 percent higher", "than"],
16 | 107: ["about 10 percent higher", "than"],
17 | 103: ["a little higher", "than"],
18 | 98: ["about the same as", ""],
19 | 94: ["a little less", "than"],
20 | 86: ["about 90 percent", "of"],
21 | 78: ["about 80 percent", "of"],
22 | 72: ["about three-quarters", "of"],
23 | 64: ["about two-thirds", "of"],
24 | 56: ["about three-fifths", "of"],
25 | 45: ["about half", ""],
26 | 37: ["about two-fifths", "of"],
27 | 30: ["about one-third", "of"],
28 | 23: ["about one-quarter", "of"],
29 | 17: ["about one-fifth", "of"],
30 | 13: ["less than a fifth", "of"],
31 | 8: ["about 10 percent", "of"],
32 | 0: ["less than 10 percent", "of"],
33 | }
34 |
35 | @register.filter
36 | def comparison_index_phrase(value):
37 | '''
38 | Each stat on the profile page can have country- and province-level
39 | values, indexed to 100 for comparisons (that is, expressed as a percentage
40 | of that statistic's value for the profile geography). That index value can
41 | be passed into this template filter to generate a comparative phrase.
42 |
43 | The COMPARISON_PHRASE_MAP defines the comparative phrases; the dict keys
44 | are the lower boundary of the range of values that result in that phrase.
45 |
46 | For example, the effective range of index values that return the phrase
47 | "about half" would be 45 to 55.
48 | '''
49 | # make sure we have an int for comparison
50 | index = round(float(value))
51 |
52 | # get lower boundaries for each phrase in the map
53 | thresholds = sorted([k for k,v in list(COMPARISON_PHRASE_MAP.items())])
54 |
55 | # get highest boundary that's less than the index value we've been passed
56 | phrase_key = max(k for k in thresholds if k <= index)
57 |
58 | phrase_bits = COMPARISON_PHRASE_MAP[phrase_key]
59 | phrase = "%s %s" % (phrase_bits[0], phrase_bits[1])
60 | return mark_safe(phrase)
61 |
62 | @register.filter
63 | def stat_type_to_number_noun(stat_type):
64 | if stat_type == 'dollar':
65 | return 'amount'
66 | elif stat_type == 'percentage':
67 | return 'rate'
68 | return 'figure'
69 |
--------------------------------------------------------------------------------
/census/templatetags/sumlevs.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from census.utils import SUMMARY_LEVEL_DICT
3 | register = template.Library()
4 |
5 | @register.filter
6 | def sumlev_name(sumlev):
7 | if SUMMARY_LEVEL_DICT[sumlev]:
8 | return SUMMARY_LEVEL_DICT[sumlev]['name']
9 | return ''
10 |
11 | @register.filter
12 | def sumlev_name_plural(sumlev):
13 | if SUMMARY_LEVEL_DICT[sumlev]:
14 | return SUMMARY_LEVEL_DICT[sumlev]['plural']
15 | return ''
16 |
17 | @register.filter
18 | def list_cut(itemlist, term):
19 | return [ i for i in itemlist if not i == term ]
20 |
--------------------------------------------------------------------------------
/census/templatetags/tabletags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 | @register.simple_tag(takes_context=True)
6 | def table_specific(context, table_id):
7 | """Safely include a fragment specific to the given table, but handle no special info gracefully."""
8 | try:
9 | fragment_path = "table/specific/%s.html" % table_id
10 | t = template.loader.get_template(fragment_path)
11 | return t.render(context)
12 | except template.TemplateDoesNotExist:
13 | return ""
14 |
--------------------------------------------------------------------------------
/census/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from .views import GeographyDetailView
3 |
4 | class ParseTestCase(TestCase):
5 | def setUp(self):
6 | self.view = GeographyDetailView()
7 |
8 | def test_parse_typical(self):
9 | """Animals that can speak are correctly identified"""
10 | (geoid,slug) = self.view.parse_fragment('16000US1714000-chicago-il')
11 | self.assertEqual(geoid,'16000US1714000')
12 | self.assertEqual(slug,'chicago-il')
13 |
14 | def test_parse_geoid(self):
15 | """Animals that can speak are correctly identified"""
16 | (geoid,slug) = self.view.parse_fragment('16000US1714000')
17 | self.assertEqual(geoid,'16000US1714000')
18 | self.assertEqual(slug,None)
19 |
20 | def test_parse_vermont(self):
21 | """Animals that can speak are correctly identified"""
22 | (geoid,slug) = self.view.parse_fragment('61000US50ADD-addison-state-senate-district-vt')
23 | self.assertEqual(geoid,'61000US50ADD')
24 | self.assertEqual(slug,'addison-state-senate-district-vt')
25 |
26 | def test_parse_problem_geoid(self):
27 | (geoid,slug) = self.view.parse_fragment('61000US50E-O')
28 | self.assertEqual(geoid,'61000US50E-O')
29 | self.assertEqual(slug,None)
30 |
31 | def test_parse_problem_geoid_and_slug(self):
32 | (geoid,slug) = self.view.parse_fragment('61000US50E-O-essex-orleans-state-senate-district-vt')
33 | self.assertEqual(geoid,'61000US50E-O')
34 | self.assertEqual(slug,'essex-orleans-state-senate-district-vt')
35 |
36 |
--------------------------------------------------------------------------------
/compose.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | server:
3 | build:
4 | context: .
5 | volumes:
6 | - .:/app
7 | ports:
8 | - 8000:5000
9 | environment:
10 | - DATABASE_URL=postgresql://wazimap:wazimap@db/wazimap
11 | depends_on:
12 | db:
13 | condition: service_healthy
14 | command: python manage.py runserver 0.0.0.0:5000
15 | db:
16 | image: postgres:11.6
17 | environment:
18 | - POSTGRES_USER=wazimap
19 | - POSTGRES_PASSWORD=wazimap
20 | - POSTGRES_DB=wazimap
21 | - PGUSER=wazimap
22 | ports:
23 | - "5433:5432"
24 | volumes:
25 | - db-data:/var/lib/postgresql/data
26 | command: ["postgres", "-c", "log_statement=all"]
27 | healthcheck:
28 | test: ["CMD-SHELL", "pg_isready", "-U postgres"]
29 | interval: 10s
30 | timeout: 5s
31 | retries: 5
32 |
33 | volumes:
34 | db-data:
35 |
--------------------------------------------------------------------------------
/deploy/.buildpacks:
--------------------------------------------------------------------------------
1 | https://github.com/cyberdelia/heroku-geo-buildpack.git
2 | https://github.com/heroku/heroku-buildpack-python.git
3 |
--------------------------------------------------------------------------------
/deploy/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn --worker-class gevent config.prod.wsgi:application -t 120 --log-file -
2 |
--------------------------------------------------------------------------------
/deploy/runtime.txt:
--------------------------------------------------------------------------------
1 | python-2.7.8
2 |
--------------------------------------------------------------------------------
/docs/customising.rst:
--------------------------------------------------------------------------------
1 | .. _customising:
2 |
3 | Customising Wazimap
4 | ===================
5 |
6 | Overriding Templates
7 | --------------------
8 |
9 | The easiest way to change page layouts and content is to add your own
10 | templates, as you would for any Django application. Most pages have ``blocks``
11 | which make it simple to override just specific parts of a page.
12 |
13 | 1. Find the existing template that you want to change by looking in the ``wazimap/templates`` directory of the wazimap package. You can also `browse the repo `_.
14 | 2. Create an empty file with the same name inside your project's ``templates`` directory.
15 | 3. Use ``{% extends %}`` to tell Django that your template extends the original Wazimap template
16 | 4. Override the page's blocks to make your changes.
17 |
18 | In the next section we provide an example.
19 |
20 | Changing the homepage
21 | ---------------------
22 |
23 | Let's say you'd like to change the homepage. You're happy with the header and the footer, but would like to change some of the text in between.
24 |
25 | The file you want to override is ``templates/homepage.html``, you can `see what it looks like in the repo `_. You want to change the text in the ``homepage_detail`` block.
26 |
27 | Create a new file in your project called ``templates/homepage.html`` that extends the existing template and provides
28 | your new content for the ``homepage_detail`` block::
29 |
30 | {% extends 'homepage.html' %}
31 |
32 | {% block homepage_detail %}
33 | your new content!
34 | {% endblock %}
35 |
36 | If you reload your site you'll see the homepage has your new content. Django uses this template instead of Wazimap's version,
37 | relying on Wazimap for the blocks you don't override.
38 |
39 | .. seealso:: There's more information on changing profile page template in :ref:`profiles`.
40 |
41 | Changing CSS theme
42 | ------------------
43 |
44 | In addition to providing your own CSS files by overriding the templates, you can also override the
45 | SCSS variables that control the Wazimap colour scheme (theme). This is the fastest and easiest
46 | way to match Wazimap's colours to your brand.
47 |
48 | .. note:: If you customise the CSS using this method, you MUST call ``python manage.py compilescss`` before running ``collectstatic`` when you deploy your application.
49 |
50 | Create a new file in your project called ``static/css/_custom.scss``. In it, override the `default Wazimap colour variables `.
51 |
52 | These are the most important ones:
53 |
54 | * ``$brand-primary-color``: a bright primary colour
55 | * ``$brand-secondary-color``: a darker, secondary colour. Variations on this colour are used throughout the site.
56 | * ``$brand-secondary-{lightest, lighter, darkest, darker}-color``: lighter and darker shades of the secondary colour. We recommend simply lightening or darkening your ``$brand-secondary-color`` by 10% and 20%.
57 | * ``$general-bg-color`` and ``$general-bg-{lightest, lighter, darkest, darker}-color``: general background colours and lighter and darker shades. We recommend simply lightening or darkening your ``$general-bg-color`` by 10% and 20%.
58 |
--------------------------------------------------------------------------------
/docs/deploying.rst:
--------------------------------------------------------------------------------
1 | .. _deploying:
2 |
3 | Deploying
4 | =========
5 |
6 | Wazimap can be deployed like any Django application. It's particularly easy to deploy on `Heroku `_ or a
7 | Heroku-like environment such as `Dokku `_. You
8 | can use the Wazimap WSGI ``application`` variable from ``wazimap.wsgi``.
9 |
10 | .. important::
11 |
12 | Always ensure that you set the ``DJANGO_SETTINGS_MODULE`` environment variable to the module path of your ``settings.py`` file.
13 |
14 | Deploying on Heroku or Dokku
15 | ----------------------------
16 |
17 | .. seealso::
18 |
19 | You can find example of the files necessary to deploy on Dokku or Heroku
20 | in the `Wazimap deploy `_ directory.
21 |
22 | To deploy on Heroku on Dokku, create your application in the normal way. Then set these config variables:
23 |
24 | * ``DATABASE_URL=``
25 | * ``DJANGO_SECRET_KEY=``
26 | * ``DJANGO_DEBUG=False``
27 | * ``DJANGO_SETTINGS_MODULE=``
28 | * ``DISABLE_COLLECTSTATIC=1=``
29 |
30 | You'll need a Procfile, too: ::
31 |
32 | web: gunicorn --worker-class gevent wazimap.wsgi:application -t 120 --log-file -
33 |
34 | GDAL
35 | ....
36 |
37 | Wazimap requires `GDAL `_ to support data downloads in formats like KML.
38 | The easiet way to get these installed on Heroku or Dokku is to use multiple
39 | buildpacks. Create a file ``.buildpacks`` in your project's root directory: ::
40 |
41 | https://github.com/cyberdelia/heroku-geo-buildpack.git
42 | https://github.com/heroku/heroku-buildpack-python.git
43 |
44 | That tells Heroku and Dokku to install GDAL and then continue with the usual Python install. Alternatively,
45 | install GDAL for your platform manually.
46 |
47 | By default, Wazimap doesn't install the libraries to use GDAL because it can be difficult to install.
48 | Tell Wazimap to install everything it needs for GDAL by installing it with ``wazimap[gdal]`` or specify
49 | ``GDAL>=1.11.0,<2.0`` in your requirements.txt.
50 |
51 | Be sure that the platform GDAL and Python GDAL versions match.
52 |
53 | Dependencies
54 | ............
55 |
56 | Wazimap requires Django 1.9.
57 |
58 | Add Wazimap and Django as dependencies for your project in your ``requirements.txt`` file: ::
59 |
60 | wazimap[gdal]
61 | Django==1.9.2
62 |
63 | Copy `Wazimap's ``app.json``` into your project: ::
64 |
65 | {
66 | "scripts": {
67 | "dokku": {
68 | "predeploy": "python manage.py compilescss && python manage.py collectstatic --noinput && rm -rf /var/tmp/wazimap_cache"
69 | }
70 | }
71 | }
72 |
73 | Finally, just use ``git push heroku`` or ``git push dokku master`` to deploy as you usually would.
74 |
75 | Should you need to do a database migration, you can run ``dokku run python manage.py migrate`` on your server.
76 |
77 | HTTPS and SSL
78 | -------------
79 |
80 | .. note:: We recommend running your website over HTTPS.
81 |
82 | We recommend that you run your site over HTTPS (SSL). If you don't use HTTPS, then any website
83 | that does use HTTPS **will not** be able to embed a chart from your Wazimap. This is because
84 | websites using HTTPS cannot load content from non-HTTPS sites.
85 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Wazimap
2 | =======
3 |
4 | Wazimap is a Django application for exploring census and other similar data. It makes it easy to understand a place
5 | through the eyes of the data, and to explore data across a range of places. It is most suited for census data
6 | but can easily be used with other data that is similarly focused on places in a country.
7 |
8 | Check out `Wazimap South Africa `_ and `Wazimap Kenya `_ to
9 | get an idea of what Wazimap is about.
10 |
11 | Wazimap is a fork of the excellent `Censusreporter `_ project which was funded by a
12 | `Knight News Challenge grant `_.
13 | You can also find `Censusreporter on GitHub `_.
14 |
15 | Wazimap builds on Censusreporter and makes it easier to re-use. Wazimap was originally built by
16 | `OpenUp `_ with the support of `Media Monitoring Africa `_.
17 | It is maintained by OpenUp.
18 |
19 | * Wazimap is on GitHub at `github.com/OpenUpSA/wazimap `_.
20 | * Wazimap is on Twitter as `@Wazimap `_.
21 |
22 | Sites using Wazimap
23 | ===================
24 |
25 | * `Wazimap South Africa `_
26 | * `Nepal Map `_
27 | * `Counting India `_
28 |
29 | License and Copyright
30 | =====================
31 |
32 | Copyright (c) 2014 Census Reporter
33 |
34 | Wazimap is licensed under the MIT License.
35 |
36 | The Wazimap name and branding is Copyright 2013-2017 Media Monitoring Africa (MMA) and may not be used without permission.
37 |
38 | If you use this software, please provide attribution to Census Reporter, Wazimap, Media Monitoring Africa and OpenUp.
39 | We recommend using this text:
40 |
41 | X is built on `Wazimap `_, an open source platform by `OpenUp `_ and `Media Monitoring Africa `_ for making census data more understandable. Wazimap is based on `Census Reporter `_ which was funded by a Knight News Challenge grant.
42 |
43 |
44 | Contents
45 | ========
46 |
47 | .. toctree::
48 | :maxdepth: 2
49 |
50 | started
51 | config
52 | geos
53 | data
54 | profiles
55 | customising
56 | deploying
57 | upgrading
58 | version_history
59 |
60 |
61 | Indices and tables
62 | ==================
63 |
64 | * :ref:`search`
65 |
--------------------------------------------------------------------------------
/docs/started.rst:
--------------------------------------------------------------------------------
1 | Getting Started
2 | ===============
3 |
4 | Wazimap is a Django application that you'll need to configure correctly before you can use it.
5 | You're going to need to import place metadata, census data and write some code to tell Wazimap
6 | what census data you want to show.
7 |
8 | Wazimap is a bit different to most Django applications. It comes with all the URLs and most
9 | of the settings that you're going to need, so you need to tell Django to load the details
10 | from the Wazimap application, rather than your Django application like normal.
11 |
12 | .. note::
13 |
14 | You'll need to choose a short name for your application. In these examples,
15 | we'll be using ``wazimap_ex`` for Wazimap Example. We suggest using
16 | the `two-letter country code `_ of your
17 | country in place of the ``ex``.
18 |
19 | 1. Setup a python virtual environment, we recommend using `virtualenv `_.
20 |
21 | 2. Install Django (version 1.8.0 or newer) and start a new Django project. ::
22 |
23 | pip install 'django<1.10'
24 | django-admin startproject wazimap_ex
25 | cd wazimap_ex
26 |
27 | 3. This will have created some files that are unnecessary because Wazimap already provides them.
28 | You can safely delete them. ::
29 |
30 | rm wazimap_ex/urls.py wazimap_ex/wsgi.py
31 |
32 | 4. Wazimap needs GDAL installed to allow users to download data in KML, GeoJSON, Excel, etc.
33 | GDAL can be complicated to install. For development, we recommend you follow the
34 | `Django instructions `_.
35 |
36 | 5. Install Wazimap and its dependencies ::
37 |
38 | pip install wazimap
39 |
40 | 6. At this point you have the absolute basics in place. We're now going to configure
41 | your Django application to load its settings from Wazimap.
42 |
43 | Change your ``settings.py`` file to import most settings from Wazimap, and then
44 | override the settings that you want to change. Replace the contents of
45 | your file with the following: ::
46 |
47 |
48 |
49 | # pull in the default wazimap settings
50 | from wazimap.settings import * # noqa
51 |
52 | # install this app before Wazimap
53 | INSTALLED_APPS = ['wazimap_ex'] + INSTALLED_APPS
54 |
55 | # Localise this instance of Wazimap
56 | WAZIMAP['name'] = 'Wazimap Example'
57 | # NB: this must be https if your site supports HTTPS.
58 | WAZIMAP['url'] = 'http://wazimap.example.com'
59 | WAZIMAP['country_code'] = 'EX'
60 |
61 | .. seealso::
62 |
63 | See the :ref:`config` section for more details on configuration options.
64 |
65 | 7. You'll need a running PostgreSQL server. The default database settings are:
66 |
67 | * Username: wazimap
68 | * Password: wazimap
69 | * Database: wazimap
70 | * Hostname: localhost
71 |
72 | So create the user and database locally: ::
73 |
74 | createuser -P wazimap
75 | createdb -O wazimap wazimap
76 |
77 | Or alternatively from inside psql: ::
78 |
79 | CREATE USER wazimap WITH PASSWORD 'wazimap';
80 | CREATE DATABASE wazimap OWNER wazimap;
81 |
82 | .. note::
83 |
84 | If you want to use a different database, configure the default database
85 | as you normally would for a Django application.
86 |
87 | 8. Run migrations to setup the database: ::
88 |
89 | python manage.py migrate
90 |
91 | 9. Create a superuser to manage your site: ::
92 |
93 | python manage.py createsuperuser
94 |
95 | 10. You now have a Wazimap application and matching database ready to load
96 | geographies and data into. Those steps are covered in the rest of this
97 | guide.
98 |
--------------------------------------------------------------------------------
/docs/upgrading.rst:
--------------------------------------------------------------------------------
1 | Upgrading
2 | =========
3 |
4 | Upgrading from 1.x to 2.x
5 | -------------------------
6 |
7 | As of version 2.0.0, Wazimap supports multiple releases of a dataset. This means that you can, for example, have two releases of a national census in Wazimap at the same time. You will need to make some changes to how you declare your tables and build profile pages.
8 |
9 | * Data table management is done through the Django admin interface.
10 | * Datasets are a concrete concept, they bring together a collection of related data tables. A dataset can have multiple releases (ie. updates) over the years. A good example of a dataset is a national census, which could be released every few years.
11 | * Data tables must have at least one release, which is a set of data for that table, released at a particular point in time.
12 |
13 | Run migrations
14 | ..............
15 |
16 | Before anything else, run migrations::
17 |
18 | python manage.py migrate
19 |
20 | Upgrade existing tables
21 | .......................
22 |
23 | The old way of declaring tables in ``tables.py`` is no longer used. Before you remove those declarations, use them to upgrade your table descriptions into the new format. Run::
24 |
25 | python manage.py upgradetables
26 |
27 | This will upgrade the tables into the database.
28 |
29 | .. note::
30 |
31 | Do not delete the data tables from the database, they are still needed and will be used by the upgraded tables.
32 |
33 | Visit the admin area
34 | ....................
35 |
36 | Create a superuser so you can login to the admin area::
37 |
38 | python manage.py createsuperuser
39 |
40 | Visit http://localhost:8000/admin to log in and check that your tables are correct.
41 |
42 | Remove the old table descriptions
43 | .................................
44 |
45 | If you're happy that your tables have been imported correctly, you can remove them from your ``tables.py`` file.
46 |
--------------------------------------------------------------------------------
/docs/version_history.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../CHANGES.rst
2 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wazimap.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django~=2.2.6
2 | SQLAlchemy<1.4
3 | boto~=2.49.0
4 | dj-database-url~=2.1.0
5 | django-cors-headers~=3.1.0
6 | django-compressor~=2.2
7 | django-pipeline~=1.6.14
8 | django-sass-processor~=0.7.3
9 | ecdsa~=0.11
10 | libsass~=0.17.0
11 | paramiko~=1.18.5
12 | psycopg2~=2.8.5
13 | requests~=2.21
14 | six~=1.12.0
15 | unicodecsv~=0.14.1
16 | whitenoise~=4.1.2
17 |
18 | gdal==3.2.2
19 | Shapely~=1.5.13
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore = E501
3 | exclude = tests/*
4 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 | from codecs import open
3 | from os import path
4 |
5 | here = path.abspath(path.dirname(__file__))
6 |
7 | # Get the long description from the relevant file
8 | with open(path.join(here, "README.rst"), encoding="utf-8") as f:
9 | long_description = f.read()
10 |
11 | with open(path.join(here, "VERSION")) as f:
12 | version = f.read().strip()
13 | setup(
14 | name="wazimap",
15 | # Versions should comply with PEP440. For a discussion on single-sourcing
16 | # the version across setup.py and the project code, see
17 | # https://packaging.python.org/en/latest/single_source_version.html
18 | version=version,
19 | description="A Django application for exploring census and other place-specific data",
20 | long_description=long_description,
21 | # The project's main homepage.
22 | url="https://github.com/OpenUpSA/wazimap",
23 | # Author details
24 | author="OpenUp",
25 | author_email="webapps@openup.org.za",
26 | # Choose your license
27 | license="MIT",
28 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
29 | classifiers=[
30 | # How mature is this project? Common values are
31 | # 3 - Alpha
32 | # 4 - Beta
33 | # 5 - Production/Stable
34 | "Development Status :: 4 - Beta",
35 | # Indicate who your project is intended for
36 | "Intended Audience :: Developers",
37 | # Pick your license as you wish (should match "license" above)
38 | "License :: OSI Approved :: MIT License",
39 | # Specify the Python versions you support here. In particular, ensure
40 | # that you indicate whether you support Python 2, Python 3 or both.
41 | "Programming Language :: Python",
42 | "Programming Language :: Python :: 3",
43 | "Programming Language :: Python :: 3.6",
44 | ],
45 | # You can just specify the packages manually here if your project is
46 | # simple. Or you can use find_packages().
47 | packages=find_packages(),
48 | include_package_data=True,
49 | # List run-time dependencies here. These will be installed by pip when
50 | # your project is installed. For an analysis of "install_requires" vs pip's
51 | # requirements files see:
52 | # https://packaging.python.org/en/latest/requirements.html
53 | install_requires=[
54 | 'Django~=2.2.6',
55 | 'SQLAlchemy~=1.2.17',
56 | 'boto~=2.49.0',
57 | 'dj-database-url~=0.5.0',
58 | 'django-cors-headers~=3.0.2',
59 | 'django-compressor~=2.2',
60 | 'django-pipeline~=1.6.14',
61 | 'django-sass-processor~=0.7.3',
62 | 'ecdsa~=0.11',
63 | 'libsass~=0.17.0',
64 | 'paramiko~=1.18.5',
65 | 'psycopg2~=2.8.5',
66 | 'requests~=2.21',
67 | 'unicodecsv~=0.14.1',
68 | 'whitenoise~=4.1.2',
69 | ],
70 |
71 | setup_requires=[
72 | "setuptools_git ~= 1.0",
73 | ],
74 |
75 | # List additional groups of dependencies here (e.g. development
76 | # dependencies). You can install these using the following syntax,
77 | # for example:
78 | # $ pip install -e .[dev,test]
79 | extras_require={
80 | 'dev': ['nose', 'flake8'],
81 | 'test': ['nose', 'flake8'],
82 | 'gdal': ['GDAL', 'Shapely~=1.5.13'],
83 | },
84 | )
85 |
--------------------------------------------------------------------------------
/wazimap/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/__init__.py
--------------------------------------------------------------------------------
/wazimap/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import Dataset, Release, SimpleTable, FieldTable, DBTable, FieldTableRelease, SimpleTableRelease
4 |
5 |
6 | admin.site.register(DBTable)
7 |
8 |
9 | class FieldTableReleaseInline(admin.StackedInline):
10 | model = FieldTableRelease
11 | fields = ('release', 'db_table')
12 | extra = 0
13 |
14 |
15 | def field_list(model):
16 | return ', '.join(model.fields)
17 | field_list.short_description = 'Fields'
18 |
19 |
20 | @admin.register(FieldTable)
21 | class FieldTableAdmin(admin.ModelAdmin):
22 | list_display = ('name', field_list, 'dataset', 'universe')
23 | list_filter = ('dataset', 'universe')
24 | search_fields = ['name']
25 | inlines = (FieldTableReleaseInline, )
26 | fieldsets = (
27 | (None, {
28 | 'fields': ('dataset', 'universe', 'fields', 'description')
29 | }),
30 | ('Advanced', {
31 | 'fields': ('name', 'stat_type', 'value_type', 'denominator_key', 'has_total'),
32 | 'classes': ('collapse', ),
33 | })
34 | )
35 |
36 |
37 | class SimpleTableReleaseInline(admin.StackedInline):
38 | model = SimpleTableRelease
39 | fields = ('release', 'db_table')
40 | extra = 0
41 |
42 |
43 | @admin.register(SimpleTable)
44 | class SimpleTableAdmin(admin.ModelAdmin):
45 | list_display = ('name', 'dataset', 'universe')
46 | list_filter = ('dataset', 'universe')
47 | inlines = (SimpleTableReleaseInline, )
48 | fieldsets = (
49 | (None, {
50 | 'fields': ('name', 'dataset', 'universe', 'description'),
51 | }),
52 | ('Advanced', {
53 | 'fields': ('stat_type', 'total_column'),
54 | 'classes': ('collapse', ),
55 | })
56 | )
57 |
58 |
59 | class ReleaseInline(admin.StackedInline):
60 | model = Release
61 | fields = ('name', 'year')
62 | extra = 0
63 | min_num = 1
64 |
65 |
66 | @admin.register(Dataset)
67 | class DatasetAdmin(admin.ModelAdmin):
68 | inlines = (ReleaseInline, )
69 |
70 |
71 | @admin.register(Release)
72 | class ReleaseAdmin(admin.ModelAdmin):
73 | list_display = ('name', 'year')
74 | list_filter = ('year',)
75 |
--------------------------------------------------------------------------------
/wazimap/apps.py:
--------------------------------------------------------------------------------
1 | from importlib import import_module
2 | import logging
3 | import os.path
4 |
5 | from django.apps import AppConfig, apps as django_apps
6 |
7 |
8 | class WazimapConfig(AppConfig):
9 | name = 'wazimap'
10 | verbose_name = "Wazimap"
11 | log = logging.getLogger(__name__)
12 |
13 | def ready(self):
14 | self.check_gdal()
15 | self.load_tables()
16 |
17 | def check_gdal(self):
18 | # GDAL is difficult to install, so we make it an optional dependency.
19 | # Here, we check if it's installed and warn if it isn't.
20 | from wazimap.geo import HAS_GDAL, gdal_missing
21 | if HAS_GDAL:
22 | import osgeo.gdal
23 | self.log.info("Wazimap found GDAL version %s" % (osgeo.gdal.VersionInfo("RELEASE_NAME")))
24 | else:
25 | gdal_missing()
26 |
27 | def load_tables(self):
28 | """ Search installed apps for tables.py and import it.
29 | This gives child Wazimap applications a chance to declare
30 | their tables.
31 | """
32 | for app in django_apps.get_app_configs():
33 | if (os.path.exists(os.path.join(app.path, 'tables.py')) or
34 | os.path.exists(os.path.join(app.path, 'tables/__init__.py'))):
35 | import_module(app.name + '.tables')
36 |
--------------------------------------------------------------------------------
/wazimap/context_processors.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 |
4 | def wazimap_settings(request):
5 | from wazimap.geo import geo_data
6 |
7 | return {
8 | 'WAZIMAP': settings.WAZIMAP,
9 | 'geo_data': geo_data
10 | }
11 |
--------------------------------------------------------------------------------
/wazimap/data/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/data/__init__.py
--------------------------------------------------------------------------------
/wazimap/data/base.py:
--------------------------------------------------------------------------------
1 | from sqlalchemy.ext.declarative import declarative_base, declared_attr
2 | from wazimap.data.utils import _metadata
3 |
4 |
5 | class Base(object):
6 | @declared_attr
7 | def __tablename__(cls):
8 | return cls.__name__.lower()
9 |
10 | def __repr__(self):
11 | return '%s(%s)' % (self.__class__.__name__,
12 | ', '.join(['%s="%s"' % (c.name, getattr(self, c.name))
13 | for c in self.__table__.columns]))
14 |
15 |
16 | Base = declarative_base(cls=Base, metadata=_metadata)
--------------------------------------------------------------------------------
/wazimap/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/management/__init__.py
--------------------------------------------------------------------------------
/wazimap/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/management/commands/__init__.py
--------------------------------------------------------------------------------
/wazimap/management/commands/upgradetables.py:
--------------------------------------------------------------------------------
1 | from django.core.management.base import BaseCommand
2 |
3 | from wazimap.models import FieldTable, SimpleTable, Dataset, Release, FieldTableRelease, SimpleTableRelease, DBTable
4 |
5 |
6 | class Command(BaseCommand):
7 | help = "Upgrades old-style Wazimap FieldTables to the version 2.0"
8 |
9 | def handle(self, *args, **options):
10 | from wazimap.data.tables import FIELD_TABLES, DATA_TABLES
11 |
12 | for table in list(FIELD_TABLES.values()):
13 | self.upgrade_field_table(table)
14 |
15 | for table in list(DATA_TABLES.values()):
16 | if not hasattr(table, 'fields'):
17 | self.upgrade_simple_table(table)
18 |
19 | def upgrade_field_table(self, table):
20 | # does it need upgrading?
21 | existing = FieldTable.objects.filter(
22 | fields=table.fields,
23 | universe=table.universe,
24 | dataset__name=table.dataset_name,
25 | ).first()
26 |
27 | if existing:
28 | self.stdout.write(self.style.WARNING("Ignoring already upgraded table '%s' that matches '%s'" % (
29 | table.id, existing)))
30 | else:
31 | # upgrade it
32 | self.stdout.write(self.style.SUCCESS("Upgrading table '%s'..." % table.id))
33 |
34 | dataset, created = Dataset.objects.get_or_create(name=table.dataset_name)
35 | if created:
36 | self.stdout.write(self.style.SUCCESS("Created dataset %s" % dataset.name))
37 |
38 | new_table = FieldTable(
39 | name=table.id,
40 | fields=table.fields,
41 | universe=table.universe,
42 | denominator_key=table.denominator_key,
43 | has_total=table.has_total,
44 | value_type=table.value_type.__name__,
45 | stat_type=table.stat_type,
46 | description=table.description,
47 | dataset=dataset,
48 | )
49 | new_table.clean()
50 | new_table.save()
51 |
52 | release, created = Release.objects.get_or_create(
53 | dataset=dataset,
54 | year=table.year,
55 | name=dataset.name,
56 | )
57 | if created:
58 | self.stdout.write(self.style.SUCCESS("Created release %s for dataset %s" % (release.year, dataset.name)))
59 |
60 | db_table, created = DBTable.objects.get_or_create(name=table.db_table)
61 | if created:
62 | self.stdout.write(self.style.SUCCESS("Created db_table entry for %s" % db_table.name))
63 |
64 | ft_release = FieldTableRelease.objects.create(data_table=new_table, db_table=db_table, release=release)
65 | self.stdout.write(self.style.SUCCESS("Created field table release %s" % ft_release))
66 |
67 | self.stdout.write(self.style.SUCCESS("Finished upgrading table '%s'" % table.id))
68 |
69 | def upgrade_simple_table(self, table):
70 | # does it need upgrading?
71 | existing = SimpleTable.objects.filter(name__iexact=table.id).first()
72 |
73 | if existing:
74 | self.stdout.write(self.style.WARNING("Ignoring already upgraded table '%s' that matches '%s'" % (
75 | table.id, existing)))
76 | else:
77 | # upgrade it
78 | self.stdout.write(self.style.SUCCESS("Upgrading table '%s'..." % table.id))
79 |
80 | dataset, created = Dataset.objects.get_or_create(name=table.dataset_name)
81 | if created:
82 | self.stdout.write(self.style.SUCCESS("Created dataset %s" % dataset.name))
83 |
84 | new_table = SimpleTable(
85 | name=table.id.lower(),
86 | universe=table.universe,
87 | stat_type=table.stat_type,
88 | description=table.description,
89 | dataset=dataset,
90 | )
91 | new_table.clean()
92 | new_table.save()
93 |
94 | release, created = Release.objects.get_or_create(
95 | dataset=dataset,
96 | year=table.year,
97 | name=dataset.name,
98 | )
99 | if created:
100 | self.stdout.write(self.style.SUCCESS("Created release %s for dataset %s" % (release.year, dataset.name)))
101 |
102 | db_table, created = DBTable.objects.get_or_create(name=table.db_table)
103 | if created:
104 | self.stdout.write(self.style.SUCCESS("Created db_table entry for %s" % db_table.name))
105 |
106 | ft_release = SimpleTableRelease.objects.create(data_table=new_table, db_table=db_table, release=release)
107 | self.stdout.write(self.style.SUCCESS("Created simple table release %s" % ft_release))
108 |
109 | self.stdout.write(self.style.SUCCESS("Finished upgrading table '%s'" % table.id))
110 |
--------------------------------------------------------------------------------
/wazimap/middleware.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.http.response import HttpResponsePermanentRedirect
3 |
4 |
5 | class RedirectMiddleware(object):
6 | def __init__(self, get_response):
7 | self.get_response = get_response
8 | # One-time configuration and initialization.
9 |
10 | def __call__(self, request):
11 | # Code to be executed for each request before
12 | # the view (and later middleware) are called.
13 |
14 | host = request.get_host()
15 |
16 | # redirect www.foo.com to foo.com?
17 | if settings.STRIP_WWW and host.startswith("www."):
18 | redirect_url = '%s://%s%s' % (request.scheme, host[4:], request.get_full_path())
19 | return HttpResponsePermanentRedirect(redirect_url)
20 |
21 | response = self.get_response(request)
22 |
23 | # Code to be executed for each request/response after
24 | # the view is called.
25 |
26 | return response
27 |
--------------------------------------------------------------------------------
/wazimap/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 |
4 | from django.db import models, migrations
5 | import wazimap.models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Geography',
16 | fields=[
17 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18 | ('geo_level', models.CharField(max_length=15)),
19 | ('geo_code', models.CharField(max_length=10)),
20 | ('name', models.CharField(max_length=20, db_index=True)),
21 | ('year', models.IntegerField(null=True, db_index=True)),
22 | ('osm_area_id', models.IntegerField(null=True, db_index=True)),
23 | ('square_kms', models.FloatField(null=True)),
24 | ('parent_level', models.CharField(max_length=15, null=True)),
25 | ('parent_code', models.CharField(max_length=10, null=True)),
26 | ],
27 | bases=(models.Model, wazimap.models.GeoMixin),
28 | ),
29 | migrations.AlterUniqueTogether(
30 | name='geography',
31 | unique_together=set([('geo_level', 'geo_code')]),
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/wazimap/migrations/0002_geography_long_name.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.2 on 2016-03-02 11:35
3 |
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='geography',
17 | name='long_name',
18 | field=models.CharField(db_index=True, max_length=100, null=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/wazimap/migrations/0003_remove_geography_osm_area_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.2 on 2016-03-02 11:37
3 |
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0002_geography_long_name'),
12 | ]
13 |
14 | operations = [
15 | migrations.RemoveField(
16 | model_name='geography',
17 | name='osm_area_id',
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/wazimap/migrations/0004_auto_20160302_1645.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.2 on 2016-03-02 14:45
3 |
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0003_remove_geography_osm_area_id'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='geography',
17 | name='name',
18 | field=models.CharField(db_index=True, max_length=100),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/wazimap/migrations/0005_unique-geo-add-year.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.13 on 2017-04-20 11:42
3 |
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0004_auto_20160302_1645'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterUniqueTogether(
16 | name='geography',
17 | unique_together=set([('geo_level', 'geo_code', 'year')]),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/wazimap/migrations/0006_geo-year-to-geo-version.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.13 on 2017-04-21 06:36
3 |
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0005_unique-geo-add-year'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='geography',
17 | name='version',
18 | field=models.CharField(db_index=True, max_length=100, null=True),
19 | ),
20 | migrations.RunSQL("""
21 | update wazimap_geography set version = year::text
22 | """),
23 | migrations.AlterUniqueTogether(
24 | name='geography',
25 | unique_together=set([('geo_level', 'geo_code', 'version')]),
26 | ),
27 | migrations.RemoveField(
28 | model_name='geography',
29 | name='year',
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/wazimap/migrations/0007_fieldtable_geoversion.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.2 on 2017-04-21 14:44
3 |
4 |
5 | from django.db import migrations
6 |
7 |
8 | from sqlalchemy import inspect
9 | from sqlalchemy.schema import AddConstraint
10 | from wazimap.data.utils import get_session
11 | from wazimap.data.tables import DATA_TABLES
12 |
13 |
14 | def forwards(apps, schema_editor):
15 | """
16 | Ensure all data tables have the new geo_version column, with a default of ''
17 | """
18 | session = get_session()
19 | inspector = inspect(session.bind)
20 |
21 | try:
22 | for data_table in list(DATA_TABLES.values()):
23 | db_model = data_table.model
24 | table = db_model.__table__
25 |
26 | cols = [c['name'] for c in inspector.get_columns(table.name)]
27 | if 'geo_version' in cols:
28 | continue
29 |
30 | # remove the old primary key constraint, if any
31 | pk = inspector.get_pk_constraint(table.name)['name']
32 | if pk:
33 | session.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (table.name, pk))
34 |
35 | # add the new column
36 | session.execute("ALTER TABLE %s ADD COLUMN geo_version VARCHAR(100) DEFAULT ''" % table.name)
37 |
38 | # add the correct new constraint
39 | session.execute(AddConstraint(table.primary_key))
40 |
41 | session.commit()
42 | finally:
43 | session.close()
44 |
45 |
46 | def reverse(apps, schema_editor):
47 | """
48 | Drop the new geo_version column from all data tables
49 | """
50 | session = get_session()
51 | inspector = inspect(session.bind)
52 |
53 | try:
54 | for data_table in list(DATA_TABLES.values()):
55 | db_model = data_table.model
56 | table = db_model.__table__
57 |
58 | # remove the primary key constraint, if any
59 | pk = inspector.get_pk_constraint(table.name)['name']
60 | if pk:
61 | session.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (table.name, pk))
62 |
63 | # drop the new column
64 | session.execute("ALTER TABLE %s DROP COLUMN geo_version" % table.name)
65 |
66 | # add the old pk constraint
67 | pk = table.primary_key
68 | pk.columns.remove(table.c.geo_version)
69 | session.execute(AddConstraint(pk))
70 |
71 | session.commit()
72 | finally:
73 | session.close()
74 |
75 |
76 | class Migration(migrations.Migration):
77 |
78 | dependencies = [
79 | ('wazimap', '0006_geo-year-to-geo-version'),
80 | ]
81 |
82 | operations = [
83 | # We no longer need this migration in 2.x
84 | # Be sure to upgrade to 1.x before 2.x if you run into issues.
85 | ]
86 |
--------------------------------------------------------------------------------
/wazimap/migrations/0008_auto_20170424_1209.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.2 on 2017-04-24 10:09
3 |
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0007_fieldtable_geoversion'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='geography',
17 | name='version',
18 | field=models.CharField(db_index=True, default=b'', max_length=100),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/wazimap/migrations/0010_null-total-column.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.2 on 2017-10-23 14:06
3 |
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0009_datatables'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterModelOptions(
16 | name='fieldtable',
17 | options={'ordering': ('name',)},
18 | ),
19 | migrations.AlterModelOptions(
20 | name='simpletable',
21 | options={'ordering': ('name',)},
22 | ),
23 | migrations.AlterField(
24 | model_name='simpletable',
25 | name='total_column',
26 | field=models.CharField(blank=True, help_text=b"Name of the column that contains the total value of all the columns in the row. Wazimap usse this to express column values as a percentage. If this is not set, the table doesn't have the concept of a total and only absolute values (not percentages) will be displayed.", max_length=50, null=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/wazimap/migrations/0011_release_citation.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.2 on 2017-12-06 09:08
3 |
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0010_null-total-column'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='release',
17 | name='citation',
18 | field=models.TextField(blank=True, help_text=b'What the user should use when citing this data.', max_length=250, null=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/wazimap/migrations/0012_auto_20190111_1342.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.13 on 2019-01-11 11:42
3 |
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('wazimap', '0011_release_citation'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='release',
17 | name='citation',
18 | field=models.TextField(blank=True, help_text=b'What the user should use when citing this data.', null=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/wazimap/migrations/0014_auto_20191021_1216.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.6 on 2019-10-21 12:16
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('wazimap', '0013_port_to_python3'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='geography',
15 | name='geo_level',
16 | field=models.CharField(max_length=25),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/wazimap/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/migrations/__init__.py
--------------------------------------------------------------------------------
/wazimap/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .geo import GeographyBase, GeoMixin, Geography # noqa
2 | from .data import FieldTable, SimpleTable, DBTable, Dataset, Release, FieldTableRelease, SimpleTableRelease # noqa
3 |
--------------------------------------------------------------------------------
/wazimap/profiles.py:
--------------------------------------------------------------------------------
1 | from collections import OrderedDict
2 | from itertools import repeat
3 |
4 | from census.profile import find_dicts_with_key
5 | from census.utils import get_ratio
6 |
7 | from wazimap.geo import geo_data
8 |
9 |
10 | def enhance_api_data(api_data):
11 | dict_list = find_dicts_with_key(api_data, 'values')
12 |
13 | for d in dict_list:
14 | raw = {}
15 | enhanced = {}
16 | geo_value = d['values']['this']
17 | num_comparatives = 2
18 |
19 | # create our containers for transformation
20 | for obj in ['values', 'error', 'numerators', 'numerator_errors']:
21 | if obj not in d:
22 | raw[obj] = dict(list(zip(geo_data.comparative_levels, repeat(0))))
23 | else:
24 | raw[obj] = d[obj]
25 | enhanced[obj] = OrderedDict()
26 | enhanced['index'] = OrderedDict()
27 | enhanced['error_ratio'] = OrderedDict()
28 | comparative_sumlevs = []
29 |
30 | # enhance
31 | for sumlevel in geo_data.comparative_levels:
32 | # add the index value for comparatives
33 | if sumlevel in raw['values']:
34 | enhanced['values'][sumlevel] = raw['values'][sumlevel]
35 | enhanced['index'][sumlevel] = get_ratio(geo_value, raw['values'][sumlevel])
36 |
37 | # add to our list of comparatives for the template to use
38 | if sumlevel != 'this':
39 | comparative_sumlevs.append(sumlevel)
40 |
41 | # add the moe ratios
42 | if (sumlevel in raw['values']) and (sumlevel in raw['error']):
43 | enhanced['error'][sumlevel] = raw['error'][sumlevel]
44 | enhanced['error_ratio'][sumlevel] = get_ratio(raw['error'][sumlevel], raw['values'][sumlevel], 3)
45 |
46 | # add the numerators and numerator_errors
47 | if sumlevel in raw['numerators']:
48 | enhanced['numerators'][sumlevel] = raw['numerators'][sumlevel]
49 |
50 | if (sumlevel in raw['numerators']) and (sumlevel in raw['numerator_errors']):
51 | enhanced['numerator_errors'][sumlevel] = raw['numerator_errors'][sumlevel]
52 |
53 | if len(enhanced['values']) >= (num_comparatives + 1):
54 | break
55 |
56 | # replace data with enhanced version
57 | for obj in ['values', 'index', 'error', 'error_ratio', 'numerators', 'numerator_errors']:
58 | d[obj] = enhanced[obj]
59 |
60 | api_data['geography']['comparatives'] = comparative_sumlevs
61 |
62 | return api_data
63 |
--------------------------------------------------------------------------------
/wazimap/static/css/_custom.scss:
--------------------------------------------------------------------------------
1 | // To customise the CSS, include a copy of this file in your Django application's
2 | // static folder and name it 'static/css/_custom.scss', then declare the variables
3 | // you'd like to override.
4 |
--------------------------------------------------------------------------------
/wazimap/static/css/_variables.scss:
--------------------------------------------------------------------------------
1 | // ---
2 | // colours
3 | // primary brand color
4 | $orange: #f55b2c !default;
5 |
6 | // secondary color and shades
7 | $teal-lightest: #a1d0c6 !default;
8 | $teal-lighter: #02aeab !default;
9 | $teal: #1a9d95 !default;
10 | $teal-darker: #027b7a !default;
11 | $teal-darkest: #014948 !default;
12 |
13 | $tan-lighter: #f7f8f3 !default;
14 | $tan: #eff1e9 !default;
15 | $tan-darker: #e0e4d4 !default;
16 | $tan-darkest: #cad0b5 !default;
17 |
18 | $grey: #aaa !default;
19 | $grey-darker: #777 !default;
20 | $grey-darkest: #222 !default;
21 |
22 | $white: #fff !default;
23 |
24 |
25 | // these two colours should complement each other:
26 | // - primary should be bright
27 | // - secondary should be darker and contrast with primary
28 | $brand-primary-color: $orange !default;
29 | $brand-primary-fg-color: $white !default;
30 | $brand-primary-bg-color: $brand-primary-color !default;
31 |
32 | $brand-secondary-lightest-color: $teal-lightest !default;
33 | $brand-secondary-lighter-color: $teal-lighter !default;
34 | $brand-secondary-color: $teal !default;
35 | $brand-secondary-darker-color: $teal-darker !default;
36 | $brand-secondary-darkest-color: $teal-darkest !default;
37 |
38 | $general-bg-lighter-color: $tan-lighter !default;
39 | $general-bg-color: $tan !default;
40 | $general-bg-darker-color: $tan-darker !default;
41 | $general-bg-darkest-color: $tan-darkest !default;
42 |
43 | $background-bg-color: $general-bg-lighter-color !default;
44 |
45 | $header-bg-color: $brand-secondary-darkest-color !default;
46 | $header-fg-color: $white !default;
47 | $footer-bg-color: $general-bg-darker-color !default;
48 |
49 | $border-color: $general-bg-darkest-color !default;
50 | $border-lighter-color: $general-bg-darker-color !default; // often used at the top of elements with $border-color at the bottom to create a shadow effect
51 |
52 | $link-color: $brand-secondary-darkest-color !default;
53 | $link-active-color: $brand-secondary-lighter-color !default;
54 |
55 | $btn-active-bg-color: $brand-primary-bg-color !default;
56 | $btn-active-fg-color: $brand-primary-fg-color !default;
57 | $btn-muted-bg-color: lighten($btn-active-bg-color, 20%) !default;
58 | $btn-muted-fg-color: $btn-active-fg-color !default;
59 |
60 | $text-highlight-color: $brand-secondary-lighter-color !default;
61 | $text-muted-color: $grey !default;
62 | // ---
63 |
64 | // --- typography
65 | $font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif !default;
66 | $font-family-secondary: "Merriweather", Georgia, serif !default;
67 | $font-family-monospace: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal !default;
68 | $font-size-base: 14px !default;
69 |
--------------------------------------------------------------------------------
/wazimap/static/css/embed.scss:
--------------------------------------------------------------------------------
1 | /* import font before calling any other CSS rules. */
2 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700);
3 |
4 | // settings
5 | @import "css/custom";
6 | @import "css/variables";
7 |
8 | // charts
9 | @import "css/charts";
10 |
--------------------------------------------------------------------------------
/wazimap/static/css/vendor/_normalize.min.scss:
--------------------------------------------------------------------------------
1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */
2 | article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:1em 40px}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}dl,menu,ol,ul{margin:1em 0}dd{margin:0 0 0 40px}menu,ol,ul{padding:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0;white-space:normal;*margin-left:-7px}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}
--------------------------------------------------------------------------------
/wazimap/static/css/wazimap.scss:
--------------------------------------------------------------------------------
1 | // settings
2 | @import "css/custom";
3 | @import "css/variables";
4 |
5 | // app
6 | @import "css/charts";
7 | @import "css/censusreporter";
8 | @import "css/app";
9 |
--------------------------------------------------------------------------------
/wazimap/static/js/maps_static.js:
--------------------------------------------------------------------------------
1 | /***
2 | * A class that loads geography boundary information from
3 | * topojson files.
4 | *
5 | * The +geometry_urls+ parameter is a map from geo levels to
6 | * URLs of a topojson or geojson file with geometry information.
7 | * A key of the empty string specifies a default URL to use for levels
8 | * not in the map.
9 | *
10 | * The easiest is to have all boundary information in one file.
11 | */
12 | function StaticGeometryLoader(geometry_urls) {
13 | var self = this;
14 |
15 | this.geometry_urls = geometry_urls;
16 | this.geometry = {};
17 |
18 | /**
19 | * Fetches geometry data for a comparison view and calls the +success+
20 | * callback with an object mapping each geo-id to a GeoJSON object.
21 | */
22 | this.loadGeometryForComparison = function(comparison, success) {
23 | this.loadGeometryForGeoIds(comparison.dataGeoIDs, comparison.geoVersion, success);
24 | };
25 |
26 | /**
27 | * Fetches geometry data for a list of geo ids (level-code)
28 | * and calls the +success+ callback with an object mapping
29 | * each geo-id to a GeoJSON object.
30 | */
31 | this.loadGeometryForGeoIds = function(geo_ids, geo_version, success) {
32 | var by_level = {};
33 |
34 | _.each(geo_ids, function(geo_id) {
35 | var parts = geo_id.split('-'),
36 | level = parts[0];
37 |
38 | if (!(level in by_level)) {
39 | by_level[level] = [];
40 | }
41 | by_level[level].push(geo_id);
42 | });
43 |
44 | // load the levels we need
45 | self.loadLevels(_.keys(by_level), geo_version, function() {
46 | var features = {};
47 |
48 | _.each(by_level, function(geo_ids, level) {
49 | _.each(geo_ids, function(geo_id) {
50 | features[geo_id] = self.geometry[geo_version][level][geo_id];
51 | });
52 | });
53 |
54 | success(features);
55 | });
56 | };
57 |
58 | this.loadGeometryForLevel = function(level, geo_version, success) {
59 | self.loadLevels([level], geo_version, function() {
60 | success({features: _.values(self.geometry[geo_version][level])});
61 | });
62 | };
63 |
64 | /**
65 | * Load the geometry data for +levels+ and then call +success+.
66 | */
67 | this.loadLevels = function(levels, geo_version, success) {
68 | var counter = levels.length;
69 |
70 | function loaded(level) {
71 | counter--;
72 | if (counter <= 0) {
73 | success();
74 | }
75 | }
76 |
77 | _.each(levels, function(level) {
78 | if (self.geometry[geo_version] && self.geometry[geo_version][level]) {
79 | // already have it
80 | loaded(level);
81 | } else {
82 | // load it remotely
83 | d3.json(self.geometry_urls[geo_version][level], function(error, json) {
84 | var features;
85 |
86 | if (error) return console.warn(error);
87 | if (json) {
88 | if (!self.geometry[geo_version]) self.geometry[geo_version] = {};
89 | self.geometry[geo_version][level] = {};
90 |
91 | if (json.type == 'Topology') {
92 | // topojson -> geojson
93 | features = topojson.feature(json, json.objects[level]);
94 | }
95 |
96 | if ('features' in features) {
97 | features = features.features;
98 | } else {
99 | features = [features];
100 | }
101 |
102 | // stash them
103 | _.each(features, function(feature) {
104 | self.geometry[geo_version][level][feature.properties.geoid] = feature;
105 | });
106 | }
107 |
108 | loaded(level);
109 | });
110 | }
111 | });
112 | };
113 | }
114 |
115 | var GeometryLoader = new StaticGeometryLoader(GEOMETRY_URLS);
116 |
--------------------------------------------------------------------------------
/wazimap/static/js/widget.geo.select.js:
--------------------------------------------------------------------------------
1 | var textmatchAPI = '/place-search/json/',
2 | geocodingAPI = 'https://maps.googleapis.com/maps/api/geocode/json?address=%QUERY&components=country:' + WAZIMAP_COUNTRY_CODE + '®ion=' + WAZIMAP_COUNTRY_CODE + '&key=' + GOOGLE_GEOCODE_API_KEY,
3 | resultTemplate = Handlebars.compile('{{geo_level}} {{full_name}}
'),
4 | geoSelect = $('#geography-select, #geography-select-home');
5 |
6 | var textMatchEngine = new Bloodhound({
7 | datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.full_name); },
8 | queryTokenizer: Bloodhound.tokenizers.whitespace,
9 | limit: 20,
10 | remote: {
11 | url: textmatchAPI,
12 | rateLimitWait: 600,
13 | replace: function (url, query) {
14 | return url += '?q=' + query;
15 | },
16 | filter: function(response) {
17 | return response.results;
18 | }
19 | }
20 | });
21 | textMatchEngine.initialize();
22 |
23 | var geocodeAddressEngine = new Bloodhound({
24 | datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.full_name); },
25 | queryTokenizer: Bloodhound.tokenizers.whitespace,
26 | limit: 5,
27 | remote: {
28 | url: geocodingAPI,
29 | rateLimitWait: 600,
30 | filter: function(response) {
31 | if (response.status != 'OK') return [];
32 |
33 | // collect coords
34 | var coords = [];
35 | for (var i = 0; i < response.results.length; i++) {
36 | var result = response.results[i];
37 |
38 | coords.push({'lat': result.geometry.location.lat, 'lng': result.geometry.location.lng});
39 | }
40 |
41 | return coords;
42 | }
43 | },
44 | });
45 | geocodeAddressEngine.initialize();
46 |
47 | var geocodeEngine = function(query, cb) {
48 | // first use google to geocode the address, handling caching,
49 | // the translate coordinates into places using our api
50 | geocodeAddressEngine.get(query, function(datums) {
51 | // now lookup places for these coords
52 | var coords = _.map(datums, function(d) { return d.lat + ',' + d.lng; });
53 | var url = textmatchAPI + '?coords=' + coords.join('&coords=');
54 | $.getJSON(url, function(response) {
55 | cb(response.results);
56 | });
57 | });
58 | };
59 |
60 | function makeGeoSelectWidget(element, selected) {
61 | element.typeahead({
62 | autoselect: true,
63 | highlight: false,
64 | hint: false,
65 | minLength: 2
66 | }, {
67 | // get textual matches from host
68 | name: 'textmatch',
69 | displayKey: 'full_name',
70 | source: textMatchEngine.ttAdapter(),
71 | limit: 20,
72 | templates: {
73 | suggestion: resultTemplate,
74 | },
75 | }, {
76 | // get geocoded matches
77 | name: 'geocoded',
78 | displayKey: 'full_name',
79 | source: geocodeEngine,
80 | limit: 20,
81 | templates: {
82 | suggestion: resultTemplate,
83 | },
84 | });
85 |
86 | element.on('typeahead:selected', selected || function(event, datum) {
87 | event.stopPropagation();
88 | window.location = '/profiles/' + datum.full_geoid + '/';
89 | });
90 | }
91 |
92 | makeGeoSelectWidget(geoSelect);
93 | makeGeoSelectWidget($('#compare-place-select'), function(event, datum) {
94 | var geoId = [profileData.geography.this.geo_level, profileData.geography.this.geo_code].join('-');
95 | event.stopPropagation();
96 | window.location = '/compare/' + geoId + '/vs/' + datum.full_geoid + '/' + '?release=' + RELEASE;
97 | });
98 |
--------------------------------------------------------------------------------
/wazimap/templates/_footer.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
15 |
25 |
26 |
27 | {% block footer_branding %}
28 |
29 |
30 |
31 |
32 |
33 |
34 | {% endblock %}
35 |
36 |
37 |
--------------------------------------------------------------------------------
/wazimap/templates/data/_base_data.html:
--------------------------------------------------------------------------------
1 | {% extends 'data/_base_data.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block body_javascript_extra %}
5 |
6 | {{ block.super }}
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/wazimap/templates/data/_blocks/_data_query_builder.html:
--------------------------------------------------------------------------------
1 |
9 |
16 |
23 |
--------------------------------------------------------------------------------
/wazimap/templates/data/data_map.html:
--------------------------------------------------------------------------------
1 | {% extends 'data/data_map.html' %}{% load humanize %}
2 | {% load staticfiles %}
3 |
4 | {% block head_css_extra %}
5 |
6 |
9 |
10 | {% endblock %}
11 |
12 | {% block body %}
13 |
14 |
21 |
22 |
23 | {% include 'data/_blocks/_toggle_menu.html' %}
24 | {% include 'data/_blocks/_topic_select_input.html' %}
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 | {% endblock %}
36 |
37 | {% block page_specific_javascript %}
38 | {{ block.super }}
39 |
40 | {% endblock %}
41 |
--------------------------------------------------------------------------------
/wazimap/templates/embed/iframe.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 | {% load sass_tags %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {% if WAZIMAP.ga_tracking_id %}
11 |
12 |
13 |
20 | {% endif %}
21 |
22 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
68 | {% include 'settings_js.html' %}
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/wazimap/templates/help.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}{% load partition staticfiles %}
2 |
3 | {% block head_title %}Help - {{ block.super }}{% endblock %}
4 |
5 | {% block content %}
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/wazimap/templates/locate/locate.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 |
3 |
4 | {% block content %}
5 |
6 |
19 | {% if places %}
20 |
32 | {% endif %}
33 |
34 | {% endblock %}
35 |
36 | {% block body_javascript_extra %}
37 |
38 |
62 | {% endblock %}
63 |
--------------------------------------------------------------------------------
/wazimap/templates/profile/_blocks/_comparative_list_item.html:
--------------------------------------------------------------------------------
1 | {% load humanize madlibs stats %}
2 | {# to be called via `comparatives` inclusion tag #}
3 |
4 | {% if index and stat_type != 'count' %}
5 | {{ index|comparison_index_phrase }} the {{ stat_type|stat_type_to_number_noun }} in {{ place_name }}: {% statvalue value %}
6 | {% if error_ratio >= 10 %}† {% endif %}
7 | {% else %}
8 | {{ place_name }}: {% statvalue value %}
9 | {% endif %}
10 | {% if numerator %}
11 | {% statvalue numerator isnumerator=True %}
12 | {% if error %}
13 | (± {% statvalue error %}{% if numerator_error %} / ±{% statvalue numerator_error isnumerator=True %}{% endif %})
14 | {% endif %}
15 | {% elif error %}
16 | ±{% statvalue error %}
17 | {% endif %}
18 |
19 |
--------------------------------------------------------------------------------
/wazimap/templates/profile/_blocks/_stat_list.html:
--------------------------------------------------------------------------------
1 | {% load humanize madlibs comparatives stats %}
2 |
3 | {% if not stat_wrapper == 'false' %}{% endif %}
4 |
5 |
6 | {% if stat.values.this == -2 %}
7 | {% statvalue 0 %}
8 | {% elif not stat.values.this %}
9 | {{ WAZIMAP.na_label }}
10 | {% else %}
11 | {% if stat_type == 'name' %}
12 | {{ stat.name }}
13 | {% else %}
14 | {% statvalue stat.values.this %}
15 | {% if stat_suffix %} {{ stat_suffix }} {% endif %}
16 | {% endif %}
17 | {% if stat.error.this_ratio >= 10 %}† {% endif %}
18 | {% endif %}
19 |
20 | {% if not stat_class == 'secondary' %}
21 | {% if stat.error.this %}
22 | ±{% statvalue stat.error.this %}
23 | {% endif %}
24 |
25 | {% if stat.numerators.this %}
26 | ({% statvalue stat.numerators.this isnumerator=True %}{% if stat.numerator_error.this %} ±{% statvalue stat.numerator_errors.this isnumerator=True %}{% endif %})
27 | {% endif %}
28 | {% endif %}
29 |
30 | {% if stat_type == 'name' %}{{ stat_name }}{% else %}{{ stat.name }}{% endif %}
31 |
32 | {% if stat_class == 'secondary' %}
33 |
34 | {% if stat.error.this %}
35 | ±{% statvalue stat.error.this %}
36 | {% endif %}
37 |
38 | {% if stat.numerators.this %}
39 | ({% statvalue stat.numerators.this isnumerator=True %}{% if stat.numerator_error.this %} ±{% statvalue stat.numerator_errors.this isnumerator=True %}{% endif %})
40 | {% endif %}
41 |
42 | {% endif %}
43 |
44 |
45 | {% if geography.comparatives and stat.values.this and not stat_class == 'secondary' %}
46 |
47 | {% for sumlev in geography.comparatives %}
48 | {% build_comparative_item sumlev stat stat_type geography decimals %}
49 | {% endfor %}
50 |
51 | {% endif %}
52 | {% if not stat_wrapper == 'false' %} {% endif %}
53 |
--------------------------------------------------------------------------------
/wazimap/templates/profile/head2head.html:
--------------------------------------------------------------------------------
1 | {% extends '_base.html' %}
2 | {% load static %}
3 |
4 | {% block head_title %}{{ geo1.name }} vs {{ geo2.name }} - {{ block.super }}{% endblock %}
5 | {% block head_facebook_tags %}
6 |
7 |
8 |
9 |
10 |
11 | {% endblock %}
12 |
13 | {% block body_class %}profile-head2head-view{% endblock %}
14 |
15 | {% block content_container %}
16 |
17 |
23 | {% endblock %}
24 |
25 | {% block body_javascript_extra %}
26 |
27 | {% endblock %}
28 |
--------------------------------------------------------------------------------
/wazimap/templates/settings_js.html:
--------------------------------------------------------------------------------
1 | {% load jsonify staticfiles %}
2 |
3 |
30 |
--------------------------------------------------------------------------------
/wazimap/templates/table/table_detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'table/_base_table.html' %}
2 | {% load tabletags static %}
3 | {% block head_title %}Table {{ table.name }}: {{ table.description }} - {{ block.super }}{% endblock %}
4 |
5 | {% block head_facebook_tags %}
6 |
7 |
8 |
9 |
10 |
11 | {#} {#}
12 | {% endblock %}
13 |
14 | {% block body_id %}table{% endblock %}
15 | {% block body_class %}data-view{% endblock %}
16 |
17 | {% block header_content %}
18 |
35 | {% endblock %}
36 |
37 | {% block content %}
38 | {% include 'data/_blocks/_topic_select_input.html' %}
39 |
40 |
41 |
44 |
45 |
46 | Table {{ table.name }}
47 | Table universe: {{ table.universe }}
48 |
49 | {% if tabulation.table_versions|length > 1 %}
50 | Other versions of this table
51 | {% for code, option in tabulation.table_versions.items %}
52 | {% if code != table.id %}{{ option.version_name }} {% endif %}
53 | {% endfor %}
54 | {% endif %}
55 |
56 |
57 |
58 |
Columns in this table
59 |
{% for column_id, column_data in table_columns.items %}
60 | {{ column_data.name }}
61 | {% endfor %}
62 |
63 |
64 |
65 |
66 | {% endblock %}
67 |
68 | {% block body_javascript_extra %}{{ block.super }}
69 |
70 |
80 | {% endblock %}
81 |
--------------------------------------------------------------------------------
/wazimap/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/templatetags/__init__.py
--------------------------------------------------------------------------------
/wazimap/templatetags/jsonify.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from django import template
4 |
5 | register = template.Library()
6 |
7 |
8 | @register.filter
9 | def jsonify(value):
10 | return json.dumps(value)
11 |
--------------------------------------------------------------------------------
/wazimap/templatetags/stats.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.utils import formats
3 | from django.template.defaultfilters import floatformat
4 | from django.contrib.humanize.templatetags.humanize import intcomma
5 | from django.conf import settings
6 |
7 | register = template.Library()
8 |
9 | CURRENCY_SYMBOL = formats.get_format('CURRENCY_SYMBOL')
10 | if CURRENCY_SYMBOL == 'CURRENCY_SYMBOL':
11 | CURRENCY_SYMBOL = '$'
12 |
13 | DECIMAL_SEPARATOR = formats.get_format('DECIMAL_SEPARATOR')
14 | DOT_ZERO = DECIMAL_SEPARATOR + "0"
15 |
16 |
17 | @register.simple_tag(takes_context=True)
18 | def statvalue(context, value, decimals=None, stat_type=None, isnumerator=False):
19 | if value is None:
20 | return settings.WAZIMAP.get('na_label', '')
21 |
22 | if decimals is None:
23 | decimals = context.get('decimals', -1)
24 | if stat_type is None:
25 | stat_type = context.get('stat_type', "number")
26 |
27 | value = intcomma(floatformat(value, decimals))
28 | if value.endswith(DOT_ZERO):
29 | value = value[:-2]
30 |
31 | if stat_type in ["dollar", "currency"]:
32 | value = CURRENCY_SYMBOL + value
33 | elif stat_type == "percentage" and not isnumerator:
34 | value = value + "%"
35 |
36 | return value
37 |
--------------------------------------------------------------------------------
/wazimap/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/tests/__init__.py
--------------------------------------------------------------------------------
/wazimap/tests/data/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenUpSA/wazimap/05075a99a73ca7e831844c567b537406bd14670d/wazimap/tests/data/__init__.py
--------------------------------------------------------------------------------
/wazimap/tests/data/test_utils.py:
--------------------------------------------------------------------------------
1 | from wazimap.tests.support import WazimapTestCase
2 | from wazimap.data.utils import get_stat_data
3 | from wazimap.data.tables import FieldTable
4 | from wazimap.geo import geo_data
5 |
6 |
7 | class UtilsTestCase(WazimapTestCase):
8 | def setUp(self):
9 | super(UtilsTestCase, self).setUp()
10 | self.geo = geo_data.geo_model(geo_level='lev', geo_code='code', version='')
11 |
12 | def test_get_stat_data_percentage(self):
13 | self.field_table(['gender'], """
14 | lev,code,Male,10
15 | lev,code,Female,20
16 | """)
17 | data, total = get_stat_data(['gender'], self.geo, self.s)
18 | self.assertEqual(total, 30)
19 | self.assertEqual(data['Male']['numerators']['this'], 10)
20 | self.assertEqual(data['Male']['values']['this'], 33.33)
21 | self.assertEqual(data['Female']['numerators']['this'], 20)
22 | self.assertEqual(data['Female']['values']['this'], 66.67)
23 |
24 | data, total = get_stat_data(['gender'], self.geo, self.s, percent=False)
25 | self.assertEqual(total, 30)
26 | self.assertIsNone(data['Male']['numerators']['this'])
27 | self.assertEqual(data['Male']['values']['this'], 10)
28 | self.assertIsNone(data['Female']['numerators']['this'])
29 | self.assertEqual(data['Female']['values']['this'], 20)
30 |
31 | def test_get_stat_data_nulls(self):
32 | self.field_table(['gender'], """
33 | lev,code,Male,10
34 | lev,code,Female,
35 | """)
36 | data, total = get_stat_data(['gender'], self.geo, self.s)
37 | self.assertEqual(total, 10)
38 | self.assertEqual(data['Male']['numerators']['this'], 10)
39 | self.assertEqual(data['Male']['values']['this'], 100)
40 | self.assertIsNone(data['Female']['numerators']['this'])
41 | self.assertIsNone(data['Female']['values']['this'])
42 |
43 | data, total = get_stat_data(['gender'], self.geo, self.s, percent=False)
44 | self.assertEqual(total, 10)
45 | self.assertIsNone(data['Male']['numerators']['this'])
46 | self.assertEqual(data['Male']['values']['this'], 10)
47 | self.assertIsNone(data['Female']['numerators']['this'])
48 | self.assertIsNone(data['Female']['values']['this'])
49 |
50 | def test_get_stat_data_nulls_with_denominator_key(self):
51 | table = self.field_table(['household goods'], None, universe='Households', denominator_key='total households')
52 | self.load_data(table, """
53 | lev,code,fridge,10
54 | lev,code,computer,5
55 | lev,code,total households,
56 | """)
57 | data, total = get_stat_data(['household goods'], self.geo, self.s)
58 | self.assertEqual(total, None)
59 | self.assertEqual(data['Fridge']['numerators']['this'], 10)
60 | self.assertIsNone(data['Fridge']['values']['this'])
61 | self.assertEqual(data['Computer']['numerators']['this'], 5)
62 | self.assertIsNone(data['Computer']['values']['this'])
63 |
--------------------------------------------------------------------------------
/wazimap/tests/support.py:
--------------------------------------------------------------------------------
1 | from django.test import TransactionTestCase
2 | from wazimap.data.utils import dataset_context
3 | from django.db import transaction
4 |
5 | from wazimap.data.utils import get_session, _engine
6 | from wazimap.models import FieldTable, Dataset, Release, DBTable
7 |
8 |
9 | class WazimapTestCase(TransactionTestCase):
10 | def setUp(self):
11 | self.s = get_session()
12 | self.ctxt = dataset_context(year='latest')
13 | self.ctxt.__enter__()
14 |
15 | def tearDown(self):
16 | self.s.close()
17 | _engine.dispose()
18 | self.ctxt.__exit__(None, None, None)
19 |
20 | def field_table(self, fields, data_str, **kwargs):
21 | with transaction.atomic():
22 | dataset, _ = Dataset.objects.get_or_create(name="Test Dataset")
23 | release, _ = Release.objects.get_or_create(name="Test release", year="2000", dataset=dataset)
24 | table = FieldTable(fields=fields, dataset=dataset, **kwargs)
25 | table.clean()
26 | table.save()
27 |
28 | db_table, _ = DBTable.objects.get_or_create(name=table.name.lower())
29 | table.release_class.objects.create(data_table=table, db_table=db_table, release=release)
30 |
31 | table.ensure_db_tables_exist()
32 |
33 | if data_str:
34 | self.load_data(table, data_str)
35 |
36 | return table
37 |
38 | def load_data(self, table, data_str):
39 | db_table = table.get_db_table(year='latest')
40 | model = db_table.model
41 |
42 | for row in data_str.strip().split("\n"):
43 | parts = row.strip().split(",")
44 | entry = model(geo_level=parts[0], geo_code=parts[1], geo_version='')
45 |
46 | # set fields
47 | for i, field in enumerate(table.fields):
48 | setattr(entry, field, parts[2 + i])
49 |
50 | v = parts[-1]
51 | v = None if v == '' else int(v)
52 | entry.total = v
53 |
54 | self.s.add(entry)
55 |
56 | self.s.flush()
57 |
--------------------------------------------------------------------------------
/wazimap/tests/test_geo.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.conf import settings
3 |
4 | from wazimap.geo import geo_data, GeoData
5 |
6 |
7 | class GeoTestCase(TestCase):
8 | def test_versioned_geos(self):
9 | # create two geos at different versions
10 | cpt11 = geo_data.geo_model.objects.create(geo_level='municipality', geo_code='cpt', long_name='City of Cape Town', version='2011')
11 | cpt16 = geo_data.geo_model.objects.create(geo_level='municipality', geo_code='cpt', long_name='City of Cape Town', version='2016')
12 |
13 | self.assertEqual(cpt16, geo_data.get_geography('cpt', 'municipality'))
14 | self.assertEqual(cpt11, geo_data.get_geography('cpt', 'municipality', '2011'))
15 | self.assertEqual(cpt16, geo_data.get_geography('cpt', 'municipality', '2016'))
16 |
17 | def test_geometry(self):
18 | # if the geometry_data is missing the version, we should raise an error
19 | settings.WAZIMAP['geometry_data'] = {'country': 'geo/country.geojson'}
20 |
21 | with self.assertRaises(ValueError):
22 | GeoData()
23 |
24 | # if the geometry_data is missing the version, we should raise an error
25 | # raises an attribute error from line 188 geo.py
26 | settings.WAZIMAP['geometry_data'] = {'': 'geo/country.geojson'}
27 |
28 | with self.assertRaises(AttributeError):
29 | GeoData()
30 |
--------------------------------------------------------------------------------
/wazimap/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for wazimap project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
8 |
9 | """
10 |
11 | import os
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wazimap.settings")
15 | application = get_wsgi_application()
16 |
--------------------------------------------------------------------------------