Skip to content

Commit

Permalink
Merge pull request #432 from onaio/fix-client-bugs-reports
Browse files Browse the repository at this point in the history
Address TallyHo Client Requests (Bugs/Enhancements)
  • Loading branch information
JohnMwashuma authored Aug 14, 2024
2 parents d78fcd9 + 20fb25c commit 1940021
Show file tree
Hide file tree
Showing 22 changed files with 335 additions and 274 deletions.
1 change: 0 additions & 1 deletion tally_ho/apps/tally/forms/recon_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.conf import settings
from tally_ho.apps.tally.management.commands.utils import (
build_generic_model_key_values_from_duckdb_row_tuple_data,
check_duplicates,
check_for_missing_columns,
get_ballot_by_ballot_number,
get_office_by_office_name_and_region_name,
Expand Down Expand Up @@ -195,6 +196,10 @@ def async_import_results_forms_from_result_forms_file(
duckdb_result_forms_data.columns,
'result_form'
)
check_duplicates(
csv_file_path=file_path,
field='barcode'
)

stations_by_number_underscore_center_code =\
{
Expand Down
41 changes: 41 additions & 0 deletions tally_ho/apps/tally/management/commands/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import duckdb
from gettext import ngettext
from django.conf import settings
from django.db import transaction
Expand Down Expand Up @@ -219,3 +220,43 @@ def check_for_missing_columns(
)
raise Exception(error_message)
return None

def check_duplicates(csv_file_path: str, field: str) -> None:
"""
Checks for duplicates in a CSV file based on a specified field using
DuckDB.
This function loads the specified CSV file into DuckDB, groups the data
by the provided field, and checks if any of the groups contain more than
one row. It returns `True` if duplicates are found, otherwise `False`.
Parameters:
----------
csv_file_path : str
The path to the CSV file to be checked.
field : str, optional
The name of the field/column to check for duplicates.
Raises:
-------
DuplicatesFoundError
If duplicates are found in the specified field.
Returns:
-------
None
If no duplicates are found.
"""
con = duckdb.connect()

query = """
SELECT {field}, COUNT(*) AS cnt
FROM read_csv_auto(?)
GROUP BY {field}
HAVING cnt > 1
""".format(field=field)

result = con.execute(query, [csv_file_path]).fetchall()

if len(result) > 0:
raise Exception(f"Duplicates found for field '{field}'")
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.2 on 2024-08-12 07:16

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('tally', '0051_electrolrace_background_image'),
]

operations = [
migrations.RemoveField(
model_name='reconciliationform',
name='number_blank_ballots',
),
]
2 changes: 0 additions & 2 deletions tally_ho/apps/tally/models/reconciliation_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ 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'))
number_valid_votes = models.PositiveIntegerField(
Expand Down
1 change: 0 additions & 1 deletion tally_ho/apps/tally/templates/data/forms.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ <h1>{{ header_text }}</h1>
<a href="{% url 'form-list' tally_id 'data_entry_2' %}">{% trans 'Data Entry 2' %}</a>
<a href="{% url 'form-list' tally_id 'correction' %}">{% trans 'Correction' %}</a>
<a href="{% url 'form-list' tally_id 'quality_control' %}">{% trans 'Quality Control' %}</a>
<a href="{% url 'form-list' tally_id 'archiving' %}">{% trans 'Archiving' %}</a>
<a href="{% url 'form-list' tally_id 'archived' %}">{% trans 'Archived' %}</a>
<a href="{% url 'form-list' tally_id 'clearance' %}">{% trans 'Clearance' %}</a>
<a href="{% url 'form-list' tally_id 'audit' %}">{% trans 'Audit' %}</a>
Expand Down
18 changes: 6 additions & 12 deletions tally_ho/apps/tally/templates/data_entry/enter_results_view.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,35 +59,29 @@ <h2>{% trans 'Reconciliation section' %}</h2>
<td width="30%" class="blue-bg"><input class="blue-bg" tabindex="110" autocomplete="off" class="required form-control" id="id_number_unstamped_ballots" name="number_unstamped_ballots" oncopy="return false;" ondrag="return false;" ondrop="return false;" onpaste="return false;" type="text"/></td>
</tr>
<tr>
<th width="10%"><span class="bignum">10</span></th>
<th width="60%"><label for="id_number_blank_ballots">
{% trans 'Number of blank ballots:' %}</label></th>
<td width="30%" class="blue-bg"><input class="blue-bg" tabindex="110" autocomplete="off" class="required form-control" id="id_number_blank_ballots" name="number_blank_ballots" oncopy="return false;" ondrag="return false;" ondrop="return false;" onpaste="return false;" type="text"/></td>
</tr>
<tr>
<th><span class="bignum">11</span></th>
<th><span class="bignum">10</span></th>
<th><label for="id_number_invalid_votes">
{% trans 'Number of invalid votes (including the blanks):' %}</label></th>
<td class="blue-bg"><input class="blue-bg" tabindex="120" autocomplete="off" class="required form-control" id="id_number_invalid_votes" name="number_invalid_votes" oncopy="return false;" ondrag="return false;" ondrop="return false;" onpaste="return false;" type="text"/></td>
</tr>
<tr>
<th><span class="bignum">12</span></th>
<th><span class="bignum">11</span></th>
<th><label for="id_number_valid_votes">
{% trans 'Number of valid votes:' %}</label></th>
<td class="blue-bg"><input class="blue-bg" tabindex="130" autocomplete="off" class="required form-control" id="id_number_valid_votes" name="number_valid_votes" oncopy="return false;" ondrag="return false;" ondrop="return false;" onpaste="return false;" type="text"/></td>
</tr>
<tr>
<th><span class="bignum">13</span></th>
<th><span class="bignum">12</span></th>
<th><label for="id_number_sorted_and_counted">
{% trans 'Total number of the sorted and counted ballots:' %}</label></th>
<td class="blue-bg"><input tabindex="140" class="blue-bg" autocomplete="off" class="required form-control" id="id_number_sorted_and_counted" name="number_sorted_and_counted" oncopy="return false;" ondrag="return false;" ondrop="return false;" onpaste="return false;" type="text"/><br/></td>
</tr>
<tr>
<td colspan="3" class="blue-bg">
{% trans 'If the figure in the field 13 does not equal the one in field 7, the totals of the fields 9, 10, 11 and 12 should be recounted.' %}
{% trans 'If the figure in the field 12 does not equal the one in field 7, the totals of the fields 9, 10 and 11 should be recounted.' %}
<br/>
<br/>
{% trans 'If after recounting, the difference persists, the ballots of the fields 9, 10, 11 and 12 should be recounted as in the manual.' %}
{% trans 'If after recounting, the difference persists, the ballots of the fields 9, 10 and 11 should be recounted as in the manual.' %}
<br/>
<br/>
{% trans 'If the difference persists, write a note in the field at the bottom of the form to explain the action taken prior to proceeding to the next step.' %}
Expand Down Expand Up @@ -117,7 +111,7 @@ <h2>{% trans 'Reconciliation section' %}</h2>
<th width="10%"><span class="bignum">2</span></th>
<th width="60%"><label for="id_number_signatures_in_vr">
<span class="helptext">
{% trans 'Number of signatures in the VR:' %}</span></label></th>
{% trans 'Number of cards in the VR:' %}</span></label></th>
<td width="30%"><input tabindex="40" autocomplete="off" class="required form-control" id="id_number_signatures_in_vr" name="number_signatures_in_vr" oncopy="return false;" ondrag="return false;" ondrop="return false;" onpaste="return false;" type="text"/></td>
</tr>
<tr>
Expand Down
18 changes: 6 additions & 12 deletions tally_ho/apps/tally/templates/quality_control/reconciliation.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,35 +42,29 @@ <h2>{% trans 'Reconciliation Section' %}</h2>
<td class="blue-bg" width="30%">{{ form.number_unstamped_ballots.data }}</td>
</tr>
<tr>
<th width="10%"><span class="bignum">10</span></th>
<th width="60%"><label for="id_number_blank_ballots">
{% trans 'Number of blank ballots:' %}</label></th>
<td class="blue-bg" width="30%">{{ form.number_blank_ballots.data }}</td>
</tr>
<tr>
<th><span class="bignum">11</span></th>
<th><span class="bignum">10</span></th>
<th><label for="id_number_invalid_votes">
{% trans 'Number of invalid votes (including the blanks):' %}</label></th>
<td class="blue-bg">{{ form.number_invalid_votes.data }}</td>
</tr>
<tr>
<th><span class="bignum">12</span></th>
<th><span class="bignum">11</span></th>
<th><label for="id_number_valid_votes">
{% trans 'Number of valid votes:' %}</label></th>
<td class="blue-bg">{{ form.number_valid_votes.data }}</td>
</tr>
<tr>
<th><span class="bignum">13</span></th>
<th><span class="bignum">12</span></th>
<th><label for="id_number_sorted_and_counted">
{% trans 'Total number of the sorted and counted ballots:' %}</label></th>
<td class="blue-bg">{{ form.number_sorted_and_counted.data }}</td>
</tr>
<tr>
<td colspan="3" class="blue-bg">
{% trans 'If the figure in the field 13 does not equal the one in field 7, the totals of the fields 9, 10, 11 and 12 should be recounted.' %}
{% trans 'If the figure in the field 12 does not equal the one in field 7, the totals of the fields 9, 10 and 11 should be recounted.' %}
<br/>
<br/>
{% trans 'If after recounting, the difference persists, the ballots of the fields 9, 10, 11 and 12 should be recounted as in the manual.' %}
{% trans 'If after recounting, the difference persists, the ballots of the fields 9, 10 and 11 should be recounted as in the manual.' %}
<br/>
<br/>
{% trans 'If the difference persists, write a note in the field at the bottom of the form to explain the action taken prior to proceeding to the next step.' %}
Expand Down Expand Up @@ -100,7 +94,7 @@ <h2>{% trans 'Reconciliation Section' %}</h2>
<th width="10%"><span class="bignum">2</span></th>
<th width="60%"><label for="id_number_signatures_in_vr">
<span class="helptext">
{% trans 'Number of signatures in the VR:' %}</span></label></th>
{% trans 'Number of cards in the VR:' %}</span></label></th>
<td width="30%">{{ form.number_signatures_in_vr.data }}</td>
</tr>
<tr>
Expand Down
48 changes: 37 additions & 11 deletions tally_ho/apps/tally/templates/reports/form_results.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
{% include "data/table.html" with export_file_name='form_results_report' server_side=True tally_id=tally_id get_centers_stations_url=get_centers_stations_url results_download_url=results_download_url languageDE=languageDE deployedSiteUrl=deployedSiteUrl %}
<script>
$(document).ready(() => {
const fileNamePostFix = () => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-based, so add 1
const day = String(now.getDate()).padStart(2, '0');
const hour = String(now.getHours()).padStart(2, '0');
const minute = String(now.getMinutes()).padStart(2, '0');
const second = String(now.getSeconds()).padStart(2, '0');
return `${year}${month}${day}_${hour}${minute}${second}`;
}
const dt_language = {{ languageDE|safe }}
const candidatesResultsReportUrl = '{{ remote_url }}'
const candidatesResultsExportReportUrl = '{{ export_url }}'
Expand Down Expand Up @@ -94,6 +104,7 @@
$('#in-report').on('click', '#filter-in-report', function () {
let data = [];
let selectOneIds = $('select#filter-in-centers').val();
let subConCodes = $('select#filter-sub-cons').val();
let selectTwoIds = $('select#filter-in-stations').val();
let exportNumber = $('input#export-number').val();
let electionLevelNames = $('select#election-level-names').val();
Expand All @@ -107,6 +118,7 @@
const items = {
select_1_ids: selectOneIds !== null ? selectOneIds : [],
select_2_ids: selectTwoIds !== null ? selectTwoIds : [],
sub_con_codes: subConCodes !== null ? subConCodes : [],
export_number: exportNumber !== null ? exportNumber : [],
election_level_names: electionLevelNames !== null ? electionLevelNames : [],
sub_race_type_names: subRaceTypeNames !== null ? subRaceTypeNames : [],
Expand Down Expand Up @@ -139,6 +151,7 @@

let data = [];
let selectOneIds = $('select#filter-in-centers').val();
let subConCodes = $('select#filter-sub-cons').val();
let selectTwoIds = $('select#filter-in-stations').val();
let electionLevelNames = $('select#election-level-names').val();
let subRaceTypeNames = $('select#sub-race-names').val();
Expand All @@ -147,7 +160,7 @@
let stationStatus = $('select#station-status').val();
let candidateStatus = $('select#candidate-status').val();
let percentageProcessed = $('input#percentage-processed').val();
const exportFileName = 'election_results.pptx'
const exportFileName = `election_results_${fileNamePostFix()}.pptx`

const downloadFile = (blob, fileName) => {
const link = document.createElement('a');
Expand All @@ -162,6 +175,7 @@

const items = {
select_1_ids: selectOneIds !== null ? selectOneIds : [],
sub_con_codes: subConCodes !== null ? subConCodes : [],
select_2_ids: selectTwoIds !== null ? selectTwoIds : [],
election_level_names: electionLevelNames !== null ? electionLevelNames : [],
sub_race_type_names: subRaceTypeNames !== null ? subRaceTypeNames : [],
Expand Down Expand Up @@ -213,6 +227,7 @@
'select#election-level-names',
'select#sub-race-names',
'select#filter-in-centers',
'select#filter-sub-cons',
'select#filter-in-stations',
'select#ballot-status',
'select#station-status',
Expand Down Expand Up @@ -256,65 +271,75 @@ <h1>{% trans 'Candidate Results' %}</h1>
</div>
</div>
<div class="col-12" style="margin-bottom: 2em;">
<div class="col-sm-3" style="padding-left: 0.5em;">
<div class="col-sm-4" style="padding-left: 0.5em;">
<p style="text-align: left; margin-left: .5em;">{% trans 'Election Levels' %}</p>
<select class="selectpicker" id="election-level-names" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Select Race Type">
{% for name in election_level_names %}
<option value={{name}}>{{name}}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-3" style="padding-left: 0.5em;">
<div class="col-sm-4" style="padding-left: 0.5em;">
<p style="text-align: left; margin-left: .5em;">{% trans 'Sub Races' %}</p>
<select class="selectpicker" id="sub-race-names" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Select Race Type">
{% for name in sub_race_type_names %}
<option value={{name}}>{{name}}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-3" style="padding-left: 0.5em;">
<div class="col-sm-4">
<p style="text-align: left; margin-left: .5em;">{% trans 'Sub Constituencies' %}</p>
<select class="selectpicker" id="filter-sub-cons" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Select Sub Constituency">
{% for sub_con in sub_cons %}
<option value={{sub_con.code}}>{{sub_con.name}}-{{sub_con.code}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-12" style="margin-bottom: 2em;">
<div class="col-sm-4" style="padding-left: 0.5em;">
<p style="text-align: left; margin-left: .5em;">{% trans 'Centers' %}</p>
<select class="selectpicker" id="filter-in-centers" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Select Centers">
{% for center in centers %}
<option value={{center.id}}>{{center.name}}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-3">
<div class="col-sm-4">
<p style="text-align: left; margin-left: .5em;">{% trans 'Stations' %}</p>
<select class="selectpicker filter-in-center-stations" id="filter-in-stations" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Select Stations">
{% for station in stations %}
<option value={{station.id}}>{{station.id}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-12" style="margin-bottom: 2em;">
<div class="col-sm-3" style="padding-left: 0.5em;">
<div class="col-sm-4" style="padding-left: 0.5em;">
<p style="text-align: left; margin-left: .5em;">{% trans 'Ballot Status' %}</p>
<select class="selectpicker ballot-status" id="ballot-status" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Ballot Status">
{% for status in ballot_status %}
<option value={{status.value}}>{{status.name}}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-3">
</div>
<div class="col-12" style="margin-bottom: 2em;">
<div class="col-sm-4">
<p style="text-align: left; margin-left: .5em;">{% trans 'Station Status' %}</p>
<select class="selectpicker station-status" id="station-status" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Station Status">
{% for status in station_status %}
<option value={{status.value}}>{{status.name}}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-3">
<div class="col-sm-4">
<p style="text-align: left; margin-left: .5em;">{% trans 'Candidate Status' %}</p>
<select class="selectpicker candidate-status" id="candidate-status" name="report_length" aria-controls="report" multiple data-actions-box="true" title="Candidate Status">
{% for status in candidate_status %}
<option value={{status.value}}>{{status.name}}</option>
{% endfor %}
</select>
</div>
<div class="col-sm-3">
<div class="col-sm-4">
<p style="text-align: left; margin-left: .5em;">{% trans 'Station Processed Percentage' %}</p>
<div>
<input style="width: 100%;" type="number" min="0" max="100" value="0" id="percentage-processed">
Expand Down Expand Up @@ -344,6 +369,7 @@ <h1>{% trans 'Candidate Results' %}</h1>
<th>{% trans 'Gender' %}</th>
<th>{% trans 'Election Level' %}</th>
<th>{% trans 'Sub Race' %}</th>
<th>{% trans 'Sub Con' %}</th>
<th>{% trans 'Order' %}</th>
<th>{% trans 'Ballot Number' %}</th>
</tr>
Expand Down
Loading

0 comments on commit 1940021

Please sign in to comment.