Code Monkey home page Code Monkey logo

nditech / apollo Goto Github PK

View Code? Open in Web Editor NEW
13.0 10.0 9.0 23.89 MB

A robust data collection and analysis tool to document political processes

Home Page: https://dem.tools/elections

License: Other

Python 23.26% HTML 13.22% CSS 6.91% JavaScript 56.24% Dockerfile 0.01% Makefile 0.03% Shell 0.17% Mako 0.01% Smarty 0.01% SCSS 0.15% Procfile 0.01%
elections election-data election-analysis election-quick-count elections-toolkit pvt political-parties

apollo's Introduction

NDI Logo

Apollo

CircleCI

Table of Contents

  1. Introduction
  2. Install
  3. Builld and Deploy
  4. Web Server Configuration
  5. Application Configuration Settings
  6. Legacy Installation Method

Apollo 3.x Deployment Guide

Introduction

This document details the steps required in deploying a fully functional installation of Apollo 3. Apollo is a data management platform to support citizen election observation and other large-scale structured data collection efforts. Developed by Tim Akinbo and his team at TimbaObjects in conjunction with NDI’s Elections team, Apollo aids the management of observers, verification of collected information, and automated aggregation for analysis. Citizen watchdogs play a critical role in validating political processes, but to be convincing must back claims with data. Elections are one of the foundations of legitimate democracy when the official results truly represent the will of the voters. Systematic election observation requires large amounts of structured information from hundreds or thousands of observers and determining what it means – fast. Apollo aids the management of observers, verification of collected information, and automated aggregation for analysis.

Install

Dependencies

The dependencies for building and deploying an Apollo 3 instance are:

  • Git - for retrieving the source code from a source code versioning repository
  • Docker - for building and deploying the Apollo 3 instance.
  • Docker-compose - also for building and deploying Apollo 3. (If need be, it is possible to deploy Apollo without this dependency using the legacy instructions for deploying found here: Legacy Installation Method)
  • NGINX - Apollo uses Nginx as a web-server for hosting the site.

Obtaining the Source

After installing git, you will be able to clone the current version of Apollo from this repo, using:

git clone https://github.com/nditech/apollo.git

After downloading, configure a settings file in the main folder called settings.ini. A basic sample settings file is shown below. The install will not work if a settings file is not created with a Secret Key specified. The secret key should be any randomly generated string of characters of similar length to the example provided. For more information on additional configuration settings, read the section "Application Configuration Settings" below.

[settings]
SECRET_KEY=sD2av35FAg43rfsDa
SSL_REQUIRED=False

Build and Deploy

In order to simplify the deployment process, Apollo now includes a docker compose application configuration to allow admins skip the entire process of having to start the database, task queue, worker and main application containers individually.

To build and start the application, simply run (from within the main folder):

sudo docker-compose up -d

The main application container and worker containers will be built and run together with the supporting database and task queue containers. After running the command initially, subsequent builds will use cached container images. To see which containers are running, use sudo docker ps.

To deploy the code from scratch without drawing upon the cached images (for example to incorporate subsequent any changes made to the Apollo code outside of the docker containers), run docker-compose build --no-cache.

Nginx configuration

After deploying the containers, the Apollo application can be accessed by configuring Nginx. After installing nginx on your server, become the root user and go to /etc/nginx/. Remove all files from the directories sites-available and sites-enabled. Create a blank file called apollo in sites-available, and create a symbolic link to a file in sites-enabled by running the command below:

ln -s /etc/nginx/sites-available/apollo /etc/nginx/sites-enabled/apollo

Then edit the apollo file in sites-available with Nginx configurations. A sample file is shown below. Replace INSERT_SITE_URL with the url for your site (replace INSERT_SITE_URL in both places). Insert your ssl crt at /etc/ssl/certs/ssl.crt and your ssl key at /etc/ssl/certs/ssl.key.

server {
    listen 80;
    server_name INSERT_SITE_URL;
    location / {
        rewrite ^ https://$server_name$request_uri? permanent;
    }
}

server {
    listen 443;
    server_name INSERT_SITE_URL;
    #limit_req zone=one burst=10 nodelay;
    #limit_req_status 429;
    ssl_trusted_certificate /etc/ssl/certs/ssl.crt;

    location / {
        proxy_pass http://localhost:5000;
        proxy_redirect http:// $scheme://;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        port_in_redirect off;
        proxy_connect_timeout 300;
        proxy_read_timeout 180;
    }

    ssl on;
    ssl_certificate /etc/ssl/certs/ssl.crt;
    ssl_certificate_key /etc/ssl/certs/ssl.key;
    ssl_session_cache    shared:SSL:10m;
    ssl_session_timeout  10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS +RC4 RC4";


    gzip on;
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    # make sure gzip does not lose large gzipped js or css files
    # see http://blog.leetsoft.com/2007/7/25/nginx-gzip-ssl
    gzip_buffers 16 8k;

    # Disable gzip for certain browsers.
    gzip_disable "MSIE [1-6].(?!.*SV1)";
}

After changing the file, restart nginx using service nginx restart (or service nginx start if it has not yet been started).

Logging in

You should now be able to log in to your site by navigating to the IP or URL for your server, or if you are running it locally without nginx by going to port 5000 on your localhost (http://localhost:5000). The default login is username: admin / password: admin. This needs to be changed upon logging in.

Application Configuration Settings

Each deployment installation can be further customized by modifying the contents of the settings.ini file. The syntax is for each setting to use the header listed below with an equal sign and then its value, for example SSL_REQUIRED=False or TIMEZONE=Africa/Lagos. Here are a collection of settings parameters and sample values together with an explanation of what they do.

SECRET_KEY (e.g. LBZyd8EY80mALqb7bl8o3da8)

The secret key contains a random string of characters, numbers and symbols and is used internally for signing and encrypting cookies and other security-related tokens. Best security practice requires that you set the value to a random value before starting the containers. Note that if this value is changed after the application is already fully configured, then user logins will stop working as the application would not be able to decrypt stored passwords. So this should remain the same throughout the lifetime of the application. This is a compulsory configuration option.

SSL_REQUIRED (e.g. False)

This parameter determines whether the application server will explicitly check if all requests are being served over a secure (https) connection. By setting this value to True, you effectively turn this check on and all connections must be secure before access is granted.

TIMEZONE (e.g. Africa/Lagos)

The timezone parameter configures the timezone that the application server uses by default. Usually this is set to the timezone of the country for which the application instance is deployed. For a full list of support timezone values, please visit this wikipedia article.

GOOGLE_TAG_MANAGER (e.g. GTM-1234567)

For digital marketing or product improvement initiatives you may want to include JavaScript and or HTML snippet tags for tracking and analytics on Apollo.

If you need to manage tags that are inserted into the application, one way to do so it to use the Google Tag Manager. This parameter allows you to set the Google Tag Manager code that is linked to the Google account from where the tags will be managed. To enable Tag Manager, modify the settings-docker.ini file. Add the configuration parameter GOOGLE_TAG_MANAGER_KEY and specify the tag manager key you get from Google and restart the container.

More information on using Google Tag Manager can be found here, and here.

REDIS_DATABASE (e.g. 0)

This value determines the redis database that is used by the application. It takes the default value of 0 but (in the unlikely event that you need to share a redis installation) it can be changed to a different value. By default most redis installations have a maximum value of 15.

REDIS_HOSTNAME (e.g. redis)

As was described above, if you require connecting to an external redis server, you can specify the value of the hostname for this redis database here.

DATABASE_HOSTNAME (e.g. postgres)

As was the case in REDIS_HOSTNAME, there might be cases where an external PostgreSQL database is to be used, setting the DATABASE_HOSTNAME allows the application and worker applications to connect to this.

DATABASE_NAME (e.g. apollo)

If you need to use the non-default database name for the installation, you can change the value here. Please note that if you are using an external PostgreSQL server, you may not need to create and link to the postgres container; in which case, simply providing the DATABASE_HOSTNAME and the DATABASE_NAME will be sufficient for the application containers to connect to the provided database.

You would also need to drop the DATABASE_NAME environment specification (-e DATABASE_NAME=...) when creating the application containers.

DEBUG (e.g. False)

Debug information is usually only used when the application is being built or when an issue is being debugged. The default value of False is sufficient and should only be changed to True if there’s the need to debug the application. Setting DEBUG to True may reveal sensitive information to the user and should only be used when actual debugging is being done and reverted to the default value when debugging is over.

MAIL_SERVER (e.g. smtp.sendgrid.net) MAIL_PORT (e.g. 587) MAIL_USE_TLS (e.g. True) MAIL_USERNAME (e.g. sendgrid-username) MAIL_PASSWORD (e.g sendgrid-apikey)

These set of configuration values allow the administrator to configure a mail server for the purpose of being able to send out transactional emails like password resets, notices on task completion, etc. It is highly recommended that these values are configured.

PROMETHEUS_SECRET (e.g. pmsecretz)

Apollo 3 provides support for an external monitoring server (Prometheus) to be able to obtain application performance metrics. In order to randomize the URL from which the stats are retrieved from, the PROMETHEUS_SECRET is added as an additional URL fragment and must be correct for the metrics to be provided. The metrics url becomes https://apollo3servername/metrics/{PROMETHEUS_SECRET}.

MAPBOX_TOKEN (e.g. pk.eyJ1I.BOh_5nWoa-sWP)

If you would rather have the better looking MapBox base tiles for the maps, you should add your MapBox API token as a parameter to the settings file.

ENABLE_SOCIAL_LOGIN (e.g. True)

If you would like to offer the option to login with either a Facebook or Google account, you should enable the social login plugin by setting this parameter to True.

GOOGLE_CLIENT_ID GOOGLE_CLIENT_SECRET

To enable Google authentication, you'll need to create credentials for the Apollo application in your Google account (https://console.developers.google.com/apis/credentials). Copy the client id and secret and set them to the GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET respectively.

FACEBOOK_CLIENT_ID FACEBOOK_CLIENT_SECRET

Similarly, to enable Facebook login, you'll need to obtain Facebook Login (https://developers.facebook.com/docs/facebook-login) credentials to use with Apollo. After setting the appropriate parameters, login with Facebook will be enabled.

API_KEY (e.g. 3a;lk9243)

The API_KEY setting enables external API access to the artefacts exposed by Apollo. This parameter specifies the key that must accompany every API request to enable access.

Legacy Installation Method

An older installation method is documented below. In almost all cases, using docker compose is preferable. However if for some reason this is not possible, use the method below instead in place of the section above labeled Installation and Deployment.

Building the Docker Images

Once the repository has been cloned, you build the application image by first changing the directory to the one containing the source code and running the command:

docker build -t apollo .

This will start the build process where all application dependencies are downloaded and installed and an application image (from which the containers will be created) will be built. Running the Application Containers

There are two essential application containers that are required in every Apollo 3 deployment. The first one is the application container - this houses the main web application and serves the application contents to the web browsers and processes all user input.

The second is the long-running task worker and is responsible for handling tasks that take a much longer time to run and that may otherwise block the main web application process and possibly timeout while waiting for the task to complete.

Supporting containers include database container (PostgreSQL) and the task queue container (Redis).

Start out by first running the database and task queue containers:

docker run -d -e POSTGRES_DB=${DATABASE_NAME:-apollo} -v postgres_data:/var/lib/postgresql/data -n postgres postgres:10-alpine docker run -d -n redis redis:4-alpine

Then you run the worker container:

docker run -d -n worker --link postgres --link redis -e DATABASE_NAME=${DATABASE_NAME:-apollo} -v upload_data:/app/uploads -v settings.ini:/app/settings.ini apollo:latest ./manage.py worker

Finally, you can then run the main application container:

docker run -d -n web --link postgres --link redis -e DATABASE_NAME=${DATABASE_NAME:-apollo} -v upload_data:/app/uploads -v ./settings.ini:/app/settings.ini apollo:latest ./manage.py gunicorn -c gunicorn.conf

In both instances, the database (PostgreSQL) and task queue (Redis) containers are linked to the worker and main application containers.

apollo's People

Contributors

blynchndi avatar delegbede avatar dependabot[bot] avatar dodumosu avatar jamesmura avatar stigsfoot avatar takinbo avatar transifex-integration[bot] avatar turnerd avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

apollo's Issues

arpeggio does not understand "=" as an option

The QA builder currently only allows one to use the "=" operator for equality matching but the arpeggio library is not configured to use that for equality matching, instead it uses "==". Either change the QA builder to use "==" or change arpeggio to use "=" for equality.

screenshot-1527833470

creating administrative divisions throws an exception

When attempting to create an administrative division, I get the following exception:

TypeError: 'has_political_code' is an invalid keyword argument for LocationType

Here's the entire traceback

Traceback (most recent call last):
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/gunicorn/workers/async.py", line 56, in handle
    self.handle_request(listener_name, req, client, addr)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/gunicorn/workers/ggevent.py", line 152, in handle_request
    super(GeventWorker, self).handle_request(*args)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/gunicorn/workers/async.py", line 107, in handle_request
    respiter = self.wsgi(environ, resp.start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/werkzeug/contrib/fixers.py", line 152, in __call__
    return self.app(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/whitenoise/base.py", line 66, in __call__
    return self.application(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_debugtoolbar/__init__.py", line 125, in dispatch_request
    return view_func(**req.view_args)
  File "/home/takinbo/p/apollo/apollo/frontend/__init__.py", line 10, in wrapper
    return f(*args, **kwargs)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_principal.py", line 199, in _decorated
    rv = f(*args, **kw)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_login/utils.py", line 261, in decorated_view
    return func(*args, **kwargs)
  File "/home/takinbo/p/apollo/apollo/locations/views_locations.py", line 248, in locations_builder
    import_graph(divisions_graph, location_set)
  File "/home/takinbo/p/apollo/apollo/locations/utils.py", line 50, in import_graph
    location_set_id=location_set.id
  File "/home/takinbo/p/apollo/apollo/dal/service.py", line 58, in create
    return self.save(self.new(**kwargs))
  File "/home/takinbo/p/apollo/apollo/dal/service.py", line 55, in new
    return self.__model__(**kwargs)
  File "<string>", line 4, in __init__
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/sqlalchemy/orm/state.py", line 417, in _initialize_instance
    manager.dispatch.init_failure(self, args, kwargs)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 187, in reraise
    raise value
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/sqlalchemy/orm/state.py", line 414, in _initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/sqlalchemy/ext/declarative/base.py", line 699, in _declarative_constructor
    (k, cls_.__name__))
TypeError: 'has_political_code' is an invalid keyword argument for LocationType

creating a critical incident from the web interface throws an error

after filling the participant and location and then filling out the critical incident form, on saving; the application crashes.

sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (psycopg2.IntegrityError) null value
in column "location_id" violates not-null constraint
DETAIL:  Failing row contains (75, 1, 1, 2, 1, null, {"A": 1, "B": 1}, null, O, 2018-03-30 17:16:09.436356, null, t, , , null, null, {}, {}, {}).
 [SQL: "INSERT INTO submission (id, deployment_id, event_id, form_id, participant_id, location_id, data, submission_type, created, updated, sender_verified, quarantine_status, verification_status, incident_description, incident_status, confidence, quality_assurance_status, overridden_fields) VALUES (nextval('submission_id_seq'), %(deployment_id)s, %(event_id)s, %(form_id)s, %(participant_id)s, %(location_id)s, %(data)s, %(submission_type)s, %(created)s, %(updated)s, %(sender_verified)s, %(quarantine_status)s, %(verification_status)s, %(incident_description)s, %(incident_status)s, %(confidence)s, %(quality_assurance_status)s, %(overridden_fields)s) RETURNING submission.id"] [parameters: {'deployment_id': 1, 'event_id': 1, 'form_id': 2, 'participant_id': 1, 'location_id': None, 'data':
'{"A": 1, "B": 1}', 'submission_type': 'O', 'created': datetime.datetime(2018, 3, 30, 17, 16, 9, 436356, tzinfo=<UTC>), 'updated': None, 'sender_verified': True, 'quarantine_status': '', 'verification_status': '', 'incident_description': None, 'incident_status': None, 'confidence': '{}', 'quality_assurance_status': '{}', 'overridden_fields': []}]

add extra data fields for locations

just like we had in participants, we should also have something similar but for locations. this should resolve

Be able to attach arbitrary additional pre-loaded fields to locations, such as EMB PS ID, or Registered Voters

in #3 although we need to have a conversation about how this will be handled by permissions as specified

Permit certain levels of user to see these pre-loaded fields

Upstream issues with MongoEngine cause random errors when logging messages

The recurrent ValidationError exception that says that we can only reference documents after they have been saved always occurs at the same point: logging a message and attaching participants and/or submissions. For some reason, MongoEngine won't update the document with an ObjectID, so trying to update the document will fail.

docker images fail to build

There is a current issue with building docker images for Apollo (currently affects 2.7.10.1 and possibly 3.x). This is likely due to some changes that may have been introduced in the base alpine linux images. An urgent fix is required.

Not all models cascade deletes

@blynchNDI reported an error deleting a participant set. On examination, the cause is that the participant set table delete does not cascade. The different models (particularly those which can be deleted via the admin) should be modified to cascade deletes when it makes sense.

fields don't map correctly to specified columns in participant upload

@dodumosu when attempting to do a participant upload, I noticed that the following fields don't map correctly: Participant ID, Phone and extra data fields. In this particular upload, the Participant ID was mapping to the phone number, Phone number did not map to the correct field (in my example it was blank) and for National ID (which was an extra data field), it was mapping to the phone number also.

improve test coverage to 80%

Current test coverage sits around 33%. This issue is to track improvements to the coverage and to get to at least 80% of the code base (where reasonable).

include forms in the user menu

in order to reduce the number of clicks required to make modifications to the forms accessible in a particular event, the forms feature should be included in the user menu.

forms should fail gracefully when no groups or fields are defined

Traceback (most recent call last):
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/werkzeug/contrib/fixers.py", line 152, in __call__
    return self.app(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/whitenoise/base.py", line 66, in __call__
    return self.application(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_debugtoolbar/__init__.py", line 125, in dispatch_request
    return view_func(**req.view_args)
  File "/home/takinbo/p/apollo/apollo/frontend/__init__.py", line 10, in wrapper
    return f(*args, **kwargs)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_login/utils.py", line 261, in decorated_view
    return func(*args, **kwargs)
  File "/home/takinbo/p/apollo/apollo/submissions/views_submissions.py", line 89, in submission_list
    filter_class = filters.make_submission_list_filter(event, form)
  File "/home/takinbo/p/apollo/apollo/submissions/filters.py", line 239, in make_submission_list_filter
    form._populate_field_cache()
  File "/home/takinbo/p/apollo/apollo/formsframework/models.py", line 98, in _populate_field_cache
    f['tag']: f for g in self.data['groups'] for f in g['fields']
TypeError: 'NoneType' object is not subscriptable

filtering by QA parameter that does a lookup on an extra data field throws an exception

Attempting to filter by a QA condition that does a lookup against an extra data field, throws an exception.

screenshot-1527834351

Exception

sqlalchemy.exc.ProgrammingError
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) operator does not exist: integer = jsonb
LINE 3: ... AND (CAST((submission.data ->> 'BB') AS INTEGER) = (partici...
                                                             ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
 [SQL: 'SELECT submission.id AS submission_id, submission.deployment_id AS submission_deployment_id, submission.event_id AS submission_event_id, submission.form_id AS submission_form_id, submission.participant_id AS submission_participant_id, submission.location_id AS submission_location_id, submission.data AS submission_data, submission.extra_data AS submission_extra_data, submission.submission_type AS submission_submission_type, submission.created AS submission_created, submission.updated AS submission_updated, submission.sender_verified AS submission_sender_verified, submission.quarantine_status AS submission_quarantine_status, submission.verification_status AS submission_verification_status, submission.incident_description AS submission_incident_description, submission.incident_status AS submission_incident_status, submission.overridden_fields AS submission_overridden_fields, submission.conflicts AS submission_conflicts \nFROM submission JOIN participant ON submission.participant_id = participant.id \nWHERE %(param_1)s = submission.form_id AND submission.submission_type = %(submission_type_1)s AND (CAST((submission.data ->> %(data_1)s) AS INTEGER) = (participant.extra_data -> %(extra_data_1)s)) = true \n LIMIT %(param_2)s OFFSET %(param_3)s'] [parameters: {'param_1': 1, 'submission_type_1': 'O', 'data_1': 'BB', 'extra_data_1': 'national', 'param_2': 25, 'param_3': 0}] (Background on this error at: http://sqlalche.me/e/f405)

TypeError: storage must be a werkzeug.FileStorage on file upload

Uploading any file to the system now returns a TypeError

127.0.0.1 - - [30/May/2018 06:44:39] "POST /locations/set/1/import HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/werkzeug/contrib/fixers.py", line 152, in __call__
    return self.app(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/whitenoise/base.py", line 66, in __call__
    return self.application(environ, start_response)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_debugtoolbar/__init__.py", line 125, in dispatch_request
    return view_func(**req.view_args)
  File "/home/takinbo/p/apollo/apollo/frontend/__init__.py", line 10, in wrapper
    return f(*args, **kwargs)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_principal.py", line 199, in _decorated
    rv = f(*args, **kw)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_login/utils.py", line 261, in decorated_view
    return func(*args, **kwargs)
  File "/home/takinbo/p/apollo/apollo/locations/views_locations.py", line 141, in locations_import
    filename = uploads.save(upload_file)
  File "/home/takinbo/.virtualenvs/apollo-nrEiZ9Wj/lib/python3.6/site-packages/flask_uploads.py", line 402, in save
    raise TypeError("storage must be a werkzeug.FileStorage")
TypeError: storage must be a werkzeug.FileStorage

change import workflow to do field mapping in reverse of what currently exists

When doing an import (e.g. participants or locations), we have internal data fields that are then mapped to columns in the import document. This isn't flexible and resorts to using things like column prefixes for fields that have multiple columns in the import document. This is a proposal to switch the import workflow to map column fields to internal data fields; this will allow us to do the following:

phone 1 -> phone number
phone 2 -> phone number
national -> sample
state -> sample

And this way we can tell that phone1 and phone 2 are columns of phone number type. We would also be able to import special data fields like this as well.

Tracking user interactions for Apollo Instances

@takinbo,

For the main branch of Apollo 3, we'll be selecting a few instances to learn user behavior in order to provide guidance for what we functionality we build, user experience we implement.

As part of this initiative to be better informed on how users are using Apollo, kindly:

Paste the Google Tag ManagerCopy the code below and paste it onto our main template to affect every Apollo page.

Paste this code as high in the of the page as possible:

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-W8V7PJF');</script>
<!-- End Google Tag Manager -->

EDIT: Wrong key ^^

Additionally, paste this code immediately after the opening tag:

<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-W8V7PJF"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

change sample imports and exports to be derived from participant imports and exports

this is dependent on #13 been done to allow mapping of those sample fields. this will then allow samples to be defined during imports of participants. The column name will represent the name of the sample. The samples management interface will then just be for deletion of samples or sample locations. Sample exports will also be included in the participants export.

Edit submission view does not load comments

Comments are not saved with a deployment, so when the submission edit view is loaded, the associated comments are not.

Possible fixes:

  • set deployment on saving comments;
  • instead of loading submission comments through their service, load directly from the submission object (i.e: use submission.comments instead of services.submission_comments.find(submission=submission)

Aha! Link: https://nditech.aha.io/features/APOLLO-201

integrate prometheus to gather metrics

The prometheus platform allows for external applications to provide endpoints for extracting metric data; by integrating an exporter in Apollo, administrators will be able to identify problems before they become critical and also have better insight into the performance of the Apollo application

When I want to create an event, I want to enable forms, participant, and location lists to be created independently of events, so that I can drag-drop into an event.

  • Be able to attach arbitrary additional pre-loaded fields to locations, such as EMB PS ID, or Registered Voters
  • Permit certain levels of user to see these pre-loaded fields
  • Permit quality assurance checks to access these pre-loaded fields
  • Be able to add or delete participants and locations directly
  • Be able to add or delete administrative divisions after the system is built
  • Be able to export and import administrative structure
  • Be able to export and import participants
  • Be able to export and import locations
  • Be able to export and import samples
  • Be able to export and import forms
  • Locations should have XY coordinate fields

Created from Aha! https://nditech.aha.io/features/APOLLO-19

Participant page for Apollo 3.1 crashes on our instance

When trying to upload participants to the Apollo 3.1 demo site (https://elections.demcloud.org) the Participants feature crashes (permanently).

Steps to reproduce:

  1. Login
  2. Nav to Participants tab
  3. Click import participants
  4. Upload CSV
  5. Map CSV

Actual results:

  • After mapping submitting participants leads to an error page with a 500
  • I have since been unable to access the Participants page.

Currently, we are re-installing Apollo and will provide you the CSV and give you access to the server to check any logs. Kindly provide Carlos with any findings you see, assuming this is related to a bad install.

Created from Aha! https://nditech.aha.io/features/APOLLO-83

do we need to split the repository into two?

We're coming up against an issue where the code for Apollo 2 and 3 have diverged. Putting up everything on one branch makes it difficult to manage changes.

We have two options:

  1. Maintain different release branches for both versions
  2. Split the project into two repositories and manage them independently

Any suggestions on what might be a better strategy for managing the project going forward?

@cdoten @stigsfoot @dodumosu

Changing user

When editing a user within the admin menu, the default is that all fields for a user are empty, including roles. Thus if you change the password for a user and click save without touching any other field, the user's roles are erased, removing the ability for the user to access most of the site. The fix would be to default the roles and permissions fields to be filled in with whatever ones the user currently has, so that if only the password is changed, the roles and permissions will stay the same and not be taken away.

implement permissions for resources

Resources are objects in the database that have been so defined as to allow for fine-grained permissions; resources include forms and extra data fields in participants and locations.

This issue is to track progress on the implementation of permissions to allow or disallow access to these resources.

Apparent bug relating to data being lost

Validate transactions don't run into conflicts for Apollo 3.x

In the second round of the Mali elections, data was submitted to Apollo and stayed there for a period of time, but later on was no longer in the database. This problem only occurred for approximately 30-50 observers and happened exclusively for responses to sections D and E of the checklist.

Created from Aha! https://nditech.aha.io/features/APOLLO-82

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.