The South African National & Provincial budget data portal by the National Treasury
This is the source code for the South African National and Provincial Budget Portal.
In addition to providing user-friendly overviews of the national and provincial
budget data, this django app provides Single Sign-on (SSO) and support for
maintaining correct and consistent data for the various tools and services
around vulekamali.
This django app used to just coordinate and serve data, hence being calleddatamanager
, but now serves the actual website, so it will be renamed appropriately.
See browserslist
We use a yarn (Node.js) and webpack-based build system for CSS and Javascript.
You can find frontend code in the following places:
budgetportal/templates
assets/js/...html
package.json
in the root, and source files in assets/js
frontend-v1.bundle.js
packages/webapp
webapp.bundle.js
assets/scss
webflow.js
and other assets are served from the webflow
app static directoryvulekamali-webflow.bundle.js
Most of the HTML has been migrated from Jekyll as simply and quickly as possible
and we’re figuring out how best to structure it going forward. Find a balance
between fitting in to the existing style and moving towards standard django
template best practises for the time being.
When vulekamali was a jekyll site, there was an attempt to have a component-
oriented file structure so that the HTML (Liquid template language), SCSS, and
Javascript for a component were all in the same folder.
When migrating to Django, we have tried to keep this structure but split out
HTML (django template language) into the django template folder, SCSS inassets/scss
and Javascript in assets/js
but ran into trouble with javascript
reading some HTML files. We decided to leave Django template include files inassets/js
for the time being and set that up as an additional template directory.
We tried using Webflow for developing the user interface for the IRM infrastructure
project pages. It offered the potential for rapid prototyping which could then be
integrated in the django project later. It turned out that frontends built using webflow
are a challenge to maintain in a normal software project like this because you can’t
create branches of work and later merge them. It’s also difficult to fork and maintain
as open source software. For the moment, only those pages are maintained from Wefblow.
Edit the infrastructure project UI by accessing the https://webflow.com/design/vulekamali
project in Webflow and see how to import those changes using the link below.
We don’t use their hosting
or CMS for production pages of vulekamali.gov.za, but rather export the HTML,
CSS and Javascript and use that for those content areas on this site. Page data
is either provided to the client via JSON embedded in the page, or via AJAX
requests to APIs.
By using webflow, we can build semi-working interactive prototypes in a convenient
design tool, refine these into mockups, export these and use it as the ready-built
frontend. There is no need for a web developer to write markup and style the page
manually. This means we can preview changes to the design in webflow, and
reflect those changes in this django site with little further frontend development.
The webflow frontend is in the Django app budgetportal.webflow
.
Webflow HTML, CSS and Javascript must not be edited manually. Those edits
will be overridden on the next import from Webflow.
See the budgetportal.webflow docs for
how to work on the webflow-based parts of the site.
We’re moving towards the following code structure:
+-------+
| views |
+-------+
| |
| V
| +-----------+
| | summaries |
| +-----------+
| | | |
| | | |
V V | V
+--------+ | +----------+ +------+
| models | | | datasets |---HTTP-->| CKAN |
+--------+ | +----------+ +------+
|
V
+--------------+ +--------------+
| openspending |---HTTP-->| OpenSpending |
+--------------+ +--------------+
A lot of summary code has been implemented as part of models but should now
start moving to summaries, and just call out to models and datasets as needed.
First time you clone this repo, and any time frontend dependencies might have changed
yarn
This is an incremental build and will rebuild the root bundle until you Ctrl+C out of it.
yarn build:dev
When it’s done with the initial build it just sits waiting to build changes looking
like this - it won’t show anything when building subsequent changes unless there
are errors:
In another shell, build the webapp package. This is not an incremental build and needs to be rebuilt for each change in this package.
yarn build:webapp
Point the hostname minio
at the host running the dev server, probably 127.0.0.1
, using your hosts
file:
127.0.0.1 localhost minio
Start the database:
docker-compose up db
Setup the database - either by running migrations against a new database, or by
loading a dump from elsewhere:
If you’re setting up a new database:
docker-compose run --rm app python manage.py migrate
docker-compose run --rm app python manage.py loaddata development-first-user
Then run the server
docker-compose up
Now you can login with initial the development superuser:
Username: admin@localhost
Password: password
A fixture is needed to set this up instead of createsuperuser
because Django Allauth is configured to require verified email addresses.
If you want to reset the database and start fresh:
docker-compose down --volumes
And then:
docker-compose up
Load an initial set of financial years, spheres and governments. You might need to add more recent ones manually in the admin interface.
You can download data from the production datamanager to use in your test environment as follows:
for year in 2016-17 2017-18 2018-19 2019-20; do curl https://vulekamali.gov.za/${year}/national/departments.csv > departments-national-${year}.csv; done
for year in 2016-17 2017-18 2018-19 2019-20; do curl https://vulekamali.gov.za/${year}/provincial/departments.csv > departments-provincial-${year}.csv; done
You can load this data into your environment with:
docker-compose run --rm app python manage.py loaddata years-spheres-governments \
video-language \
events \
homepage \
quarters \
menu
for year in 2016-17 2017-18 2018-19 2019-20; do docker-compose run --rm app python manage.py load_departments ${year} national departments-national-${year}.csv; done
for year in 2016-17 2017-18 2018-19 2019-20; do docker-compose run --rm app python manage.py load_departments ${year} provincial departments-provincial-${year}.csv; done
Finally load the 2019-20 featured national infrastructure projects on the Infrastructure Project Parts admin page via the Import buton. Find the file to import at budgetportal/fixtures/infrastructure-projects-2019-20.csv
.
When modifying Django models, make migrations with
docker-compose run --rm app python manage.py makemigrations
If you’re running your development environment in docker, a good way to maintain python dependencies is
# On a shell in your host machine
docker-compose run --rm -u0 app bash
# On the root bash shell in the container:
poetry update
# On a shell in your host machine
docker-compose build app worker
python manage.py runserver_plus
Format your code using Black (See version in github actions):
docker-compose run —rm app black budgetportal manage.py discourse performance
TBC
budgetportal/static/javascript/app.js
budgetportal/static/stylesheets/app.scss
bower install -Sp package-to-install
See package.json and webpack.config.js
See README.md
Specifically, it is set up so that development can happen both directly against the webapp, e.g.
cd packages/webapp
yarn start
or
cd packages/webapp
yarn start:storybook
Or as part of the django app where it gets embedded
yarn build:webapp
and reloading the django page.
To use the Single Sign-on functionality in local development, make sure you set the relevant environment variables to match your local setup, e.g.
HTTP_PROTOCOL=http \
DISCOURSE_SSO_SECRET=... \
CKAN_SSO_URL=http://localhost/user/login \
EMAIL_HOST=localhost \
EMAIL_PORT=2525 \
EMAIL_USE_TLS= \
python manage.py runserver
Logout from ckan might not send you to the right URL to logout from DataManager. You can manually go to logout from this app at http://localhost:8000/accounts/logout/.
Environment variable | Description |
---|---|
DEBUG_CACHE |
Enable the django app cache. Normally disabled by DEBUG=True , this enables it for development - see more in settings.py . |
ROBOTS_DENY_ALL |
Set to true to disallow robots, e.g. to avoid sandbox getting indexed by google |
GUNICORN_WORKERS_ARG |
e.g. --workers 2 - use this to specify the number of workers gunicorn should run. |
All tests
docker-compose run --rm app python manage.py test
Specific tests, e.g.
docker-compose run --rm app python manage.py test budgetportal.tests.test_bulk_upload.BulkUploadTestCase
You’ll need:
national-treasury
organisationCreate and configure the app
dokku apps:create budgetportal
dokku config:set budgetportal DJANGO_DEBUG=false \
DISABLE_COLLECTSTATIC=1 \
DJANGO_SECRET_KEY=some-secret-key \
CKAN_API_KEY=... \
DATABASE_URL=postgresql://... \
EMAIL_HOST_PASSWORD=... \
DISCOURSE_SSO_SECRET=... \
RECAPTCHA_PRIVATE_KEY=... \
SENTRY_DSN=https://5999094b0e214151bdfef41a5ad513c2@sentry.io/1730285 \
AWS_ACCESS_KEY_ID=... \
AWS_SECRET_ACCESS_KEY=... \
AWS_STORAGE_BUCKET_NAME=... \
SOLR_URL=... \
COMMENTS_ENABLED=True
git push dokku master
dokku run budgetportal python manage.py migrate
dokku run budgetportal python manage.py createsuperuser
dokku ps:scale budgetportal worker=1
Also use dokku domains
to configure the hostnames that your app will serve.
Apart from the superuser, additional users authenticate using either username+password or OAuth with social media accounts, e.g. Google and Facebook.
To enable this, we use django-allauth add social media account providers which provide verified email addresses in Django Admin’s Social Accounts section.
For Google, set up an OAuth Client ID in Google API Console
The departments and their metadata are loaded using the Django Manage Command load_departments
.
The command can be called to update the production database using
DATABASE_URL=postgresql://...produser...:...prodpasswd...@proddbhost/budgetportal python manage.py load_departments 2018-19 national departments.csv
where
2018-19
is the financial year slugnational
is the sphere slugdepartments.csv
is a CSV file as follows:Required columns:
government
- government namedepartment_name
vote_number
Optional columns:
is_vote_primary
- TRUE or FALSEintro
website_url
Markdown syntax must be used for formatting intro
. e.g. 2 line breaks will result in new paragraphs. Use headings like ## Vote purpose
e.g. for national
| government | department_name | vote_number | is_vote_primary | intro | website_url |
|——————|————————-|——————-|————————-|———-|——————-|
| South Africa | The Presidency | 1 | TRUE | ## Vote purpose
Facilitate a common programme towards the …
## Mandate
To serve the president in the execution of his … | http://www.thepresidency.gov.za/ |
| South Africa | Centre for Public Service Innovation | 10 | FALSE | ## Vote purpose
Facilitate the unearthing, development and practical …
## Mandate
The responsibility for public sector innovation is vested in the Minister of Public Service… | www.cpsi.co.za |
e.g. for provincial
government | department_name | vote_number | is_vote_primary | intro | website_url |
---|---|---|---|---|---|
Eastern Cape | Health | 3 | TRUE | ## Vision A quality health service to the people of the … ## Mission To provide and ensure accessible comprehensive integrated … ## Core functions and responsibilities The strategic objectives are in line with the implementation |
If you add a field to datamanager/budgetportal/search_indexes.py
then you need to update the Solr schema for it to be available. In dev these commands will update it:
docker compose run app python manage.py build_solr_schema --filename schema.xml
docker cp schema.xml datamanager-solr-1:/opt/solr/server/solr/budgetportal/conf/schema.xml
You will need to update the vulekamali/solr-dokku
repo with the updated schema.xml
too.
cores/budgetportal/conf/schema.xml
You can verify the updated schema using the Solr Admin e.g. http://localhost:8983/solr/#/budgetportal/schema?field=NEW_FIELD_NAME
You can use Haystack to rebuild the index:docker compose run app python manage.py haystack_update_index -b 10
https://django-haystack.readthedocs.io/en/latest/management_commands.html#rebuild-index
MIT License