Skip to content

Commit

Permalink
Merge aab68a6 into 8bbde10
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnMwashuma committed Jul 8, 2020
2 parents 8bbde10 + aab68a6 commit cbc2d14
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 19 deletions.
9 changes: 6 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
addons:
postgresql: '9.6'
postgresql: "9.6"
language: python
python:
- '3.6.5'
- "3.6.5"
install:
- pip install -r requirements/common.pip
- pip install coverage
- pip install coveralls
- pip install flake8
- pip freeze # list our pip library versions
before_script:
- psql -c 'CREATE DATABASE tally;' -U postgres
script:
- python manage.py test
- coverage run --source=. manage.py test
after_success:
- coveralls
notifications:
slack: onaio:snkNXgprD498qQv4DgRREKJF
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
[![Build Status](https://travis-ci.org/onaio/tally-ho.svg?branch=master)](https://travis-ci.org/onaio/tally-ho)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d610ecc632d340ff822c577054fdff38)](https://app.codacy.com/gh/onaio/tally-ho)
[![Coverage Status](https://coveralls.io/repos/github/onaio/tally-ho/badge.svg?branch=master)](https://coveralls.io/github/onaio/tally-ho?branch=master)

## Tally-Ho!

Election results data entry and verification software built by [Ona Systems](http://company.ona.io) and commissioned by the Libyan [High National Elections Commission](http://hnec.ly/) and the [United Nations Development Program](http://www.undp.org).

## Quick install
Expand All @@ -18,7 +20,7 @@ Prerequisites: this assumes you have [virtualenvwrapper](http://virtualenvwrappe

```bash
mkvirtualenv tally --python=python3.6.5
pip install -r requirements/dev.pip
pip install -r requirements/dev.pip
```

### Quick start with user demo data
Expand Down Expand Up @@ -48,7 +50,7 @@ python manage.py runserver --settings=tally_ho.settings.dev
> This will only work if you have data files in the folder `./data`
The first argument is the database user, the second is the database host IP
address, and the third is the settings file. Modify these arguments as needed.
address, and the third is the settings file. Modify these arguments as needed.

```bash
./scripts/reload_all postgres 127.0.0.1 tally_ho.settings.common
Expand Down Expand Up @@ -81,11 +83,13 @@ Pass the `-s` option if you want to use `ipdb.set_trace()` to debug during a tes
The below assumes you have `pip` installed `requirements/dev.pip`.

Generate model graph for all models:

```
python manage.py graph_models --settings=tally_ho.settings.dev --pydot -a -g -o tally-ho-all-models.png
```

Generate model graph for app models:

```
python manage.py graph_models --settings=tally_ho.settings.dev --pydot -a -X GroupObjectPermission,UserObjectPermission,GroupObjectPermissionBase,BaseGenericObjectPermission,UserObjectPermissionBase,BaseObjectPermission,Version,Revision,Pageview,Visitor,Session,AbstractBaseSession,Site,LogEntry,User,Group,AbstractUser,Permission,ContentType,AbstractBaseUser,PermissionsMixin,BaseModel -g -o tally-ho-app-models.png
```
Expand All @@ -100,5 +104,5 @@ The `MAX_FILE_UPLOAD_SIZE` variable in `tally_ho/settings/common.py` file define

## News

* This is an [article about tally-ho](http://blog.ona.io/general/2014/06/12/Tally-Ho-Robust-Open-Source-Election-Software.html) and its use in Libya.
* This presentation at PyConZA 2014 about the project, [Writing Python Code to Decide an Election](https://blog.ona.io/general/2016/02/12/writing-python-code-to-decide-an-election.html).
- This is an [article about tally-ho](http://blog.ona.io/general/2014/06/12/Tally-Ho-Robust-Open-Source-Election-Software.html) and its use in Libya.
- This presentation at PyConZA 2014 about the project, [Writing Python Code to Decide an Election](https://blog.ona.io/general/2016/02/12/writing-python-code-to-decide-an-election.html).
21 changes: 21 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,28 @@
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
"tally_ho.settings.default")
is_testing = 'test' in sys.argv
if is_testing:
import coverage
cov = coverage.coverage(
source=['.'],
omit=['*/tests/*',
'manage.py',
'tally_ho/wsgi.py',
'*/migrations/*',
'*/settings/*',
'*/templates/*'])
cov.set_option('report:show_missing', True)
cov.set_option('report:skip_covered', True)
cov.erase()
cov.start()

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)

if is_testing:
cov.stop()
cov.save()
cov.html_report(directory='covhtml')
cov.report()
1 change: 1 addition & 0 deletions requirements/dev.pip
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-r common.pip
coverage
coveralls
django-debug-toolbar
django_extensions
flake8
Expand Down
1 change: 1 addition & 0 deletions tally_ho/apps/tally/forms/recon_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Meta:
'number_ballots_inside_box',
'number_ballots_inside_and_outside_box',
'number_unstamped_ballots',
'number_blank_ballots',
'number_invalid_votes',
'number_valid_votes',
'number_sorted_and_counted',
Expand Down
23 changes: 23 additions & 0 deletions tally_ho/apps/tally/migrations/0031_auto_20200707_0723.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 2.1.1 on 2020-07-07 07:23

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tally', '0030_quarantinecheck_active'),
]

operations = [
migrations.AddField(
model_name='reconciliationform',
name='number_blank_ballots',
field=models.PositiveIntegerField(blank=True, default=0, null=True, verbose_name='Number of blank ballots'),
),
migrations.AlterField(
model_name='reconciliationform',
name='number_invalid_votes',
field=models.PositiveIntegerField(verbose_name='Number of invalid votes'),
),
]
4 changes: 3 additions & 1 deletion tally_ho/apps/tally/models/reconciliation_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ class Meta:
_('Total number of ballots found inside and outside the ballot box'))
number_unstamped_ballots = models.PositiveIntegerField(
_('Number of unstamped ballots'))
number_blank_ballots = models.PositiveIntegerField(
_('Number of blank ballots'), null=True, blank=True, default=0)
number_invalid_votes = models.PositiveIntegerField(
_('Number of invalid votes (including the blanks)'))
_('Number of invalid votes'))
number_valid_votes = models.PositiveIntegerField(
_('Number of valid votes'))
number_sorted_and_counted = models.PositiveIntegerField(
Expand Down
50 changes: 39 additions & 11 deletions tally_ho/libs/verify/quarantine_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ def quarantine_checks():
'pass_turnout_percentage_validation':
pass_turnout_percentage_validation,
'pass_percentage_of_votes_per_candidate_validation':
pass_percentage_of_votes_per_candidate_validation}
pass_percentage_of_votes_per_candidate_validation,
'pass_percentage_of_blank_ballots_trigger':
pass_percentage_of_blank_ballots_trigger}
methods = []

quarantine_checks_methods =\
Expand Down Expand Up @@ -317,12 +319,39 @@ def pass_invalid_ballots_percentage_validation(result_form):

qc = QuarantineCheck.objects.get(
method='pass_invalid_ballots_percentage_validation')
invalid_ballots_percantage =\
(recon_form.number_invalid_votes /
recon_form.number_ballots_inside_the_box) * 100
allowed_invalid_ballots_percantage = qc.percentage
invalid_ballots_percentage =\
100 * (recon_form.number_invalid_votes /
recon_form.number_ballots_inside_the_box)
allowed_invalid_ballots_percentage = qc.percentage

return invalid_ballots_percantage <= allowed_invalid_ballots_percantage
return invalid_ballots_percentage <= allowed_invalid_ballots_percentage


def pass_percentage_of_blank_ballots_trigger(result_form):
"""Validate the percentage of blank ballots.
If the `result_form` does not have a `reconciliation_form` this will
always return True.
Fails if the percentage of blank ballots is greater than the this
trigger percentage value.
:param result_form: The result form to check.
:returns: A boolean of true if passed, otherwise false.
"""
recon_form = result_form.reconciliationform

if not recon_form:
return True

qc = QuarantineCheck.objects.get(
method='pass_percentage_of_blank_ballots_trigger')
blank_ballots_percentage =\
100 * (recon_form.number_blank_ballots /
recon_form.number_ballots_inside_the_box)
allowed_blank_ballots_percentage = qc.percentage

return blank_ballots_percentage <= allowed_blank_ballots_percentage


def pass_turnout_percentage_validation(result_form):
Expand Down Expand Up @@ -354,11 +383,10 @@ def pass_turnout_percentage_validation(result_form):
qc = QuarantineCheck.objects.get(
method='pass_turnout_percentage_validation')

turnout_percantage =\
(recon_form.number_ballots_used / registrants) * 100
allowed_turnout_percantage = qc.percentage
turnout_percentage = 100 * (recon_form.number_ballots_used / registrants)
allowed_turnout_percentage = qc.percentage

return turnout_percantage <= allowed_turnout_percantage
return turnout_percentage <= allowed_turnout_percentage


def pass_percentage_of_votes_per_candidate_validation(result_form):
Expand Down Expand Up @@ -389,7 +417,7 @@ def pass_percentage_of_votes_per_candidate_validation(result_form):
candidate.num_votes(result_form=result_form,
form_state=FormState.QUALITY_CONTROL)
candidate_votes_percentage =\
(candidate_votes/total_candidates_votes) * 100
100 * (candidate_votes/total_candidates_votes)

if candidate_votes_percentage > allowed_candidate_votes_percentage:
return False
Expand Down
7 changes: 7 additions & 0 deletions tally_ho/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
SITE_ID = 1

NOSE_ARGS = [
'--cover-erase',
'--with-coverage',
'--cover-package=tally_ho'
]
Expand Down Expand Up @@ -227,4 +228,10 @@
'active': False,
'value': 50,
'percentage': 50},
{'name':
'Trigger 10 - Validate percentage of blank ballots',
'method': 'pass_percentage_of_blank_ballots_trigger',
'active': False,
'value': 80,
'percentage': 20},
]

0 comments on commit cbc2d14

Please sign in to comment.