Coder Social home page Coder Social logo

openupsa / pmg-cms-2 Goto Github PK

View Code? Open in Web Editor NEW
6.0 14.0 7.0 151.66 MB

Content Management System for the Parliamentary Monitoring Group · https://www.pivotaltracker.com/n/projects/1367366

Home Page: https://pmg.org.za/

License: Apache License 2.0

Python 30.03% Mako 0.02% CSS 1.73% JavaScript 5.03% HTML 54.77% Shell 0.02% Less 3.40% SCSS 4.96% Procfile 0.01% Dockerfile 0.03%
python parliamentary-monitoring parliament open-data

pmg-cms-2's Introduction

Parliamentary Monitoring Group website

Parliamentary monitoring application for use by the Parliamentary Monitoring Group in Cape Town, South Africa. See: https://www.pmg.org.za.

What does this project do

Allow citizens and other interested parties to monitor what's going on in the South African parliament. With specific focus on tracking the progress of legislation as it moves through the various phases: from being introduced for the first time to finally being approved and signed into law.

The purpose of the project is to improve parliamentary oversight, make the parliamentary process more accessible and improve transparency surrounding the activities of parliament.

How it works

The project consists of the following major components:

  • User-facing website, including free and paid-for content (built using Flask, Jinja2 templates, Bootstrap and jQuery)
  • Database (PostgreSQL)
  • Search engine (Elastic Search)
  • Admin interface (Flask-Admin, integration with Mandrill for email notifications)
  • API (Flask)
    • https://api.pmg.org.za
    • When this web app connects to its own API, it always connects to 127.0.0.1:5000 and sends the Host header of the api. subdomain of SERVER_HOST to avoid routing "dogfooding" traffic to the outside internet.

Making use of the API

All of the data that is displayed through the frontend website, is served through an API at https://api.pmg.org.za which is freely accessible. However, please note that access to some content on the frontend website is restricted, and the same restrictions apply for the API.

Contributing to the project

This project is open-source, and anyone is welcome to contribute. If you just want to make us aware of a bug / make a feature request, then please add a new GitHub Issue (if a similar one does not already exist).

NOTE: On 2015-07-05 we removed some very large files from the repo and its history, reducing the size of the repo from over 100MB to 30MB. This required re-writing the history of the repo. You must pull and rebase your changes.

If you want to contribute to the code, please fork the repository, make your changes, and create a pull request.

Local setup

Build the necessary services:

docker compose build

Setup the database:

docker compose run web python setup_dev_database.py
docker compose run web python app.py db stamp head
docker compose run web python bin/search.py --reindex all

Add the following lines to your .hosts file:

127.0.0.1 pmg.test
127.0.0.1 api.pmg.test

Start the server:

docker compose up

You should now see it running at http://pmg.test:5000/ and http://api.pmg.test:5000/.

You can login with:

user : admin
password : admin

Each time you pull in changes that might contain database changes:

docker compose run web python app.py db migrate
docker compose run web python app.py db upgrade

To delete the database for a completely fresh setup, run:

docker compose down --volumes

To start the task scheduler, run:

docker compose run web python app.py start_scheduler

Developing email features

Run a local mock SMTP server on port 2525

Set the SMTP environment variables

source env.localmail

Running tests

docker compose -f docker-compose.yml -f docker-compose-test.yml run --rm web nosetests tests --exe -v

Code formatting

We use Black to format our code. You can install it using pip install black and run it with:

black app.py bin config pmg tests

Deployment instructions

Deployment is to dokku, a Heroku-like environment. To deploy, simply push to the git remote:

git push dokku

Sensitive or environment-specific configuration variables are set as environment variables using dokku config:set, the important ones are:

  • SERVER_NAME - Flask uses this as the base hostname and port for the server - Flask Blueprint subdomains base from this. If it can't match the Host header in requests to this, it serves a 404 response.
    • pmg.org.za in production
    • pmg.test:5000 in development
    • Flask seems to use this for generating absolute URLs, except when the X-Forwarded-Host header is provided, in which case that hostname is used for absolute URLs.
  • FRONTEND_HOST - It's not currently clear if this is used anywhere
    • https://pmg.org.za/ in production
    • http://pmg.test:5000/ in development
  • SESSION_COOKIE_DOMAIN
    • pmg.org.za in production
    • pmg.test in dev
  • SQLALCHEMY_DATABASE_URI
  • FLASK_ENV=production
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • SENDGRID_API_KEY
  • MAIL_PASSWORD
  • SECURITY_PASSWORD_SALT
  • RUN_PERIODIC_TASKS=true
  • SOUNDCLOUD_APP_KEY_ID
  • SOUNDCLOUD_APP_KEY_SECRET
  • SOUNDCLOUD_USERNAME
  • SOUNDCLOUD_PASSWORD
  • SOUNDCLOUD_PERIOD_HOURS=6
  • MAX_SOUNDCLOUD_BATCH=10
  • S3_BUCKET=pmg-assets
  • STATIC_HOST=https://static.pmg.org.za/ or http://pmg-assets.s3-website-eu-west-1.amazonaws.com/

Reindexing for Search

To re-index all content for search, run:

ssh [email protected] run python bin/search.py --reindex all

This isn't normally necessary as the search index is updated as items are created, updated and deleted. It can be useful when the index has become out of date. Search functionality will fail while the indexing is in progress. Re-indexing takes about 10 minutes.

Task scheduler

Dokku won't automatically start the task scheduler process. To start it, run:

dokku ps:scale pmg worker=1

Database migration

We use Flask-Migrate and Alembic for applying changes to the data model. To setup a migration script:

python app.py db migrate -m "<revision description>"

Then to run the script on your local machine:

python app.py db upgrade

Updating parliamentary days

PMG needs to know the individual days in which Parliament sat, for each year. It uses this information to calculate the number of parliamentary days that it took for bills to be adopted. It reads these days from the file data/parliament-sitting-days.txt.

Updating this information is a two-step process:

  1. Update the spreadsheet data/parliament-sitting-days.xlsx that lists the days parliament sits
  2. Run python bin/load_parliamentary_days --pm-days data/parliament-sitting-days.xlsx to update data/parliament-sitting-days.txt
  3. Run git diff to sanity check the changes
  4. Commit the changes

Caching

Application-level caching is used for certain views, initially based on which views the server spends most time on as seen in NewRelic Transaction overview.

To add caching to a view, add the following decorator - it must be the decorator closest to the view method so that it caches the view result, and not the result from other decorators:

from pmg import cache, cache_key, should_skip_cache
...
@cache.memoize(make_name=lambda fname: cache_key(request),
               unless=lambda: should_skip_cache(request, current_user))

Arguments:

  • unless must be true when the cache should not be used. Frontend (views.py) views must always use this because the view shows them as logged in, even on pages where the rest of the data is the same. API views that don't serve subscription data or have any user-specific data don't need it.
  • make_name must be the cache key for the view. It's very important that query strings are taken into consideration for the cache key.

pmg-cms-2's People

Contributors

delenamalan avatar dependabot[bot] avatar desafinadude avatar guushoekman avatar havanhuy1997 avatar j-norwood-young avatar jbothma avatar knightebsuku avatar longhotsummer avatar lunga001 avatar michaelglenister avatar morabaraba avatar paulmwatson avatar petrus-jvrensburg avatar pidelport avatar smmbll avatar waracci avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pmg-cms-2's Issues

installation error

Hi, please tell me how fix this:

notroot@dhcppc5:~$ cd pmg-cms-2/
notroot@dhcppc5:~/pmg-cms-2$ virtualenv --no-site-packages env
New python executable in env/bin/python
Installing setuptools, pip...done.
notroot@dhcppc5:~/pmg-cms-2$ source env/bin/activate
(env)notroot@dhcppc5:~/pmg-cms-2$ pip install -r requirements.txt
Downloading/unpacking git+http://github.com/Code4SA/[email protected] (from -r requirements.txt (line 9))
  Cloning http://github.com/Code4SA/flask-security.git (to 2.0.2) to /tmp/pip-kbcKV0-build
  Running setup.py (path:/tmp/pip-kbcKV0-build/setup.py) egg_info for package from git+http://github.com/Code4SA/[email protected]

    warning: no previously-included files matching '*' found under directory 'tests/__pycache__'
Downloading/unpacking Fabric==1.10.0 (from -r requirements.txt (line 1))
  Downloading Fabric-1.10.0.tar.gz (208kB): 208kB downloaded
  Running setup.py (path:/home/notroot/pmg-cms-2/env/build/Fabric/setup.py) egg_info for package Fabric

    warning: no previously-included files matching '*' found under directory 'sites/docs/_build'
    warning: no previously-included files matching '*' found under directory 'sites/www/_build'
    warning: no previously-included files matching '*.pyc' found under directory 'tests'
    warning: no previously-included files matching '*.pyo' found under directory 'tests'
Downloading/unpacking Flask==0.10.1 (from -r requirements.txt (line 2))
  Downloading Flask-0.10.1.tar.gz (544kB): 544kB downloaded
  Running setup.py (path:/home/notroot/pmg-cms-2/env/build/Flask/setup.py) egg_info for package Flask

    warning: no files found matching '*' under directory 'tests'
    warning: no previously-included files matching '*.pyc' found under directory 'docs'
    warning: no previously-included files matching '*.pyo' found under directory 'docs'
    warning: no previously-included files matching '*.pyc' found under directory 'tests'
    warning: no previously-included files matching '*.pyo' found under directory 'tests'
    warning: no previously-included files matching '*.pyc' found under directory 'examples'
    warning: no previously-included files matching '*.pyo' found under directory 'examples'
    no previously-included directories found matching 'docs/_build'
    no previously-included directories found matching 'docs/_themes/.git'
Downloading/unpacking Flask-Admin==1.0.9.dev0 (from -r requirements.txt (line 3))
  Could not find a version that satisfies the requirement Flask-Admin==1.0.9.dev0 (from -r requirements.txt (line 3)) (from versions: 0.1.3.linux-x86_64, 0.1.1, 0.1.2, 0.1.2, 0.1.3, 0.1.3, 0.1.4, 0.1, 0.2.0, 0.2.1, 0.2.2, 0.3.0, 0.4.0, 0.4.1, 0.4.2, 1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.0.6, 1.0.7, 1.0.8, 1.0.9)
  Some externally hosted files were ignored (use --allow-external to allow).
Cleaning up...
No distributions matching the version for Flask-Admin==1.0.9.dev0 (from -r requirements.txt (line 3))
Storing debug log for failure in /home/notroot/.pip/pip.log

Object relationships in API responses are inconsistent and unexpected

We don't explicitly detail what should be returned by the API ito object relationships. This means that when you ask for https://api.pmg.org.za/committee/77/ for example, you get tabled committee reports and events nested, but not other nested relationships. This is both confusing and, at times, annoying if you don't want all that extra data.

Our serialisation code is home-grown and has weird edge cases like loops.

I'd like us to re-explore how we do serialisation in the API and be more explicit about it, with these goals:

  • we're explicit and consistent about returning nested relationships vs links.
  • use a 3rd party serialisation/schema package like marshmallow which has adapters for Flask and SQLAlchemy
  • allow the caller to specify what fields they want ala JSONAPI's sparse fieldsets. This is useful when we, say, want to load titles, dates and ids of all committee meetings for a particular committee, but don't want the summary and body (which are big). Marshmallow has rich support for this
  • version the new api under /v2/ or similar.

I've looked at Flask Restless but it's undergoing a major rewrite and there are a few others but nothing is very promising. So, rather than totally rewriting, I think we should focus on achieving the changes above which have benefit to us in the short term.

New installation error

Running setup.py (path:/home/notroot/pmg-cms-2/env/build/psycopg2/setup.py) egg_info for package psycopg2
    running egg_info
    creating pip-egg-info/psycopg2.egg-info
    writing dependency_links to pip-egg-info/psycopg2.egg-info/dependency_links.txt
    writing pip-egg-info/psycopg2.egg-info/PKG-INFO
    writing top-level names to pip-egg-info/psycopg2.egg-info/top_level.txt
    writing manifest file 'pip-egg-info/psycopg2.egg-info/SOURCES.txt'
    warning: manifest_maker: standard file '-c' not found

    Error: pg_config executable not found.

    Please add the directory containing pg_config to the PATH
    or specify the full executable path with the option:

        python setup.py build_ext --pg-config /path/to/pg_config build ...

    or with the pg_config option in 'setup.cfg'.
    Complete output from command python setup.py egg_info:
    running egg_info

creating pip-egg-info/psycopg2.egg-info

writing dependency_links to pip-egg-info/psycopg2.egg-info/dependency_links.txt

writing pip-egg-info/psycopg2.egg-info/PKG-INFO

writing top-level names to pip-egg-info/psycopg2.egg-info/top_level.txt

writing manifest file 'pip-egg-info/psycopg2.egg-info/SOURCES.txt'

warning: manifest_maker: standard file '-c' not found



Error: pg_config executable not found.



Please add the directory containing pg_config to the PATH

or specify the full executable path with the option:



    python setup.py build_ext --pg-config /path/to/pg_config build ...



or with the pg_config option in 'setup.cfg'.

----------------------------------------
Cleaning up...
  Removing temporary dir /home/notroot/pmg-cms-2/env/build...
Command python setup.py egg_info failed with error code 1 in /home/notroot/pmg-cms-2/env/build/psycopg2
Exception information:
Traceback (most recent call last):
  File "/home/notroot/pmg-cms-2/env/local/lib/python2.7/site-packages/pip/basecommand.py", line 122, in main
    status = self.run(options, args)
  File "/home/notroot/pmg-cms-2/env/local/lib/python2.7/site-packages/pip/commands/install.py", line 278, in run
    requirement_set.prepare_files(finder, force_root_egg_info=self.bundle, bundle=self.bundle)
  File "/home/notroot/pmg-cms-2/env/local/lib/python2.7/site-packages/pip/req.py", line 1229, in prepare_files
    req_to_install.run_egg_info()
  File "/home/notroot/pmg-cms-2/env/local/lib/python2.7/site-packages/pip/req.py", line 325, in run_egg_info
    command_desc='python setup.py egg_info')
  File "/home/notroot/pmg-cms-2/env/local/lib/python2.7/site-packages/pip/util.py", line 697, in call_subprocess
    % (command_desc, proc.returncode, cwd))
InstallationError: Command python setup.py egg_info failed with error code 1 in /home/notroot/pmg-cms-2/env/build/psycopg2

Inconsistent handling of timezone offsets

Some committee meetings appear on the wrong date because of inconsistent handling of the timezone offset.

Really old meetings appear at times like:

1997-09-15 00:00:00+00:00

But newer ones appear one day early, at times like:

2006-11-14 23:00:00+00:00
2014-02-17 22:00:00+00:00

Exact search

Is it possible to search an exact phrase using quotations? e.g. "Support staff for members i.e. executive secretary"

Sort search results by date

I am currently search for results in Hansards. I know that I am looking for a result in October 2009. I can filter by year but not by month. This means that I need to scan through the results. It would be much quicker if the results were sorted by date so that I can skip to the correct page in the results.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.