diff --git a/README.md b/README.md index ed680ad5b..7100ae4a0 100644 --- a/README.md +++ b/README.md @@ -1,139 +1,166 @@ +# Tally-Ho + ![Build Status](https://github.com/onaio/tally-ho/actions/workflows/config.yml/badge.svg?branch=master) [![codecov](https://codecov.io/github/onaio/tally-ho/branch/master/graph/badge.svg?token=1PR3KIqgr6)](https://codecov.io/github/onaio/tally-ho) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/1e817ebba18946fa84cb129cdc914f0b)](https://app.codacy.com/gh/onaio/tally-ho/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) -## Tally-Ho! +## Overview -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). +Election results data entry and verification software built by [Ona Systems](http://company.ona.io), commissioned by the Libyan [High National Elections Commission](http://hnec.ly/) and [UNDP](http://www.undp.org). -## Quick install +## Quick Install -### Checkout the repos +### Checkout the Repository ```bash git clone git@github.com:onaio/tally-ho.git ``` -### Make a virtual environment and install requirements +### Set Up Virtual Environment and Install Requirements -Prerequisites: this assumes you have [virtualenvwrapper](http://virtualenvwrapper.readthedocs.org/en/latest/install.html) and [PostgreSQL](https://wiki.postgresql.org/wiki/Detailed_installation_guides) installed. +Prerequisites: Ensure [virtualenvwrapper](http://virtualenvwrapper.readthedocs.org/en/latest/install.html) and [PostgreSQL](https://wiki.postgresql.org/wiki/Detailed_installation_guides) are installed. ```bash mkvirtualenv tally --python=python3.9 pip install -r requirements/dev.pip ``` -Install `libpq-dev` library that contains a minimal set of `PostgreSQL`_ binaries and headers requried -for building 3rd-party applications for `PostgreSQL`_. +Install `libpq-dev` for PostgreSQL headers: + ```bash sudo apt-get install libpq-dev ``` -Install memcache -```bash -sudo apt-get update && sudo apt-get install -y memcached -``` +Install memcached and Redis: -Install redis ```bash -sudo apt-get install -y redis-server +sudo apt-get update && sudo apt-get install -y memcached redis-server ``` -Make sure you have the latest versions of pip, wheel, and setuptools installed, run +Ensure latest versions of pip, wheel, and setuptools: + ```bash python -m pip install -U pip wheel setuptools ``` -To Enable [pre-commit hook checks](https://pre-commit.com/#3-install-the-git-hook-scripts) for development, in your virtual env, run +Enable [pre-commit hook checks](https://pre-commit.com/#3-install-the-git-hook-scripts): + ```bash pre-commit install ``` -### Running celery +### Running Celery ```bash celery -A tally_ho.celeryapp worker --loglevel=info ``` -### Quick start with user demo data -> This will remove all data in the database. +### Quick Start with User Demo Data -To create the database, load demo users, and start the server all in one, run +> **Warning**: This will erase all database data. ```bash ./scripts/quick_start ``` -If you've aleady setup the server, you can start the server with +If server setup is complete, start the server: ```bash python manage.py runserver --settings=tally_ho.settings.dev ``` -### Loading tally demo data - -> TODO: add some demo result forms and candidate lists +### Loading Tally Demo Data -### Advanced: recreate the database, then load the data and demo users +> **Note**: Add demo result forms and candidate lists. -> This will remove all data in the database. +### Advanced: Recreate Database and Load Demo Users -> 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. +> **Warning**: This erases all database data and only works with files in `./data`. ```bash ./scripts/reload_all postgres 127.0.0.1 tally_ho.settings.common ``` -## Docker Install +## Docker Installation -If you already have Docker and `docker-compose` installed on your machine you can quickly have a demo up by changing into the checked out code directory and running: +With Docker and `docker-compose` installed, build and run: ```bash docker-compose build docker-compose up ``` -You can now visit the site at `127.0.0.1:8000`. - -If you want to use Docker to run the site in production you will need to: +Visit `127.0.0.1:8000`. For production, modify the `docker-compose.yml` file: -1. modify the `docker-compose.yml` file to change the NGINX listening port from 8000 to 80, -2. add the host you are running the site on to a new `ALLOWED_HOSTS` list in the `tally_ho/settings/docker.py` file. +1. Change NGINX port from 8000 to 80. +2. Add your host to `ALLOWED_HOSTS` in `tally_ho/settings/docker.py`. ## Running Tests -run `pytest tally_ho` +Run tests with: + +```bash +pytest tally_ho +``` ## Documentation +### Arabic Translations + +Follow these steps for managing Arabic translations: + +1. **Add Arabic Language**: Update `settings.py`: + + ```python + # settings.py + LANGUAGES = [ + ('en', 'English'), + ('ar', 'Arabic'), + ] + + # Ensure LANGUAGE_CODE is set to a default language (e.g., 'en') + LANGUAGE_CODE = 'en' + ``` + +2. **Generate Arabic Translation Files**: + + ```bash + django-admin makemessages -l ar + ``` + +3. **Edit Arabic Translations**: Update `locale/ar/LC_MESSAGES/django.po`. + +4. **Compile Translations**: + + ```bash + django-admin compilemessages + ``` + ### Generating Model Graphs -The below assumes you have `pip` installed `requirements/dev.pip` and [graphviz](https://graphviz.org/download/) in your machine. +Install requirements from `requirements/dev.pip` and [graphviz](https://graphviz.org/download/). -Generate model graph for all models: +Generate all model graphs: -``` +```bash 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: +Generate specific app model graphs: -``` -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 +```bash +python manage.py graph_models --settings=tally_ho.settings.dev --pydot -a -X GroupObjectPermission,... -g -o tally-ho-app-models.png ``` ### Demo Users -The `create_demo_users` command will create demo users for each role with usernames like `super_administrator`, and password `data`. +Use the `create_demo_users` command to create demo users with usernames like `super_administrator`, and password `data`. ### File Uploads -The `MAX_FILE_UPLOAD_SIZE` variable in `tally_ho/settings/common.py` file defines the file upload limit which is currently set to 10MB. +File upload limit is set to 10MB in `MAX_FILE_UPLOAD_SIZE` within `tally_ho/settings/common.py`. ## News -- This is an [article about tally-ho](https://ona.io/home/writing-python-code-to-decide-an-election-2/) and its use in Libya. -- This presentation at PyConZA 2014 about the project, [Writing Python Code to Decide an Election](https://ona.io/home/writing-python-code-to-decide-an-election-2/). +- Article: [Writing Python Code to Decide an Election](https://ona.io/home/writing-python-code-to-decide-an-election-2/). +- PyConZA 2014 presentation: [Writing Python Code to Decide an Election](https://ona.io/home/writing-python-code-to-decide-an-election-2/). diff --git a/docs/overview/README.md b/docs/overview/README.md index 4d1c515ce..a0eb709e3 100644 --- a/docs/overview/README.md +++ b/docs/overview/README.md @@ -1,4 +1,4 @@ -## System Overview +# System Overview **tally-system** is used to record, verify, and report the results of paper votes that have been used in an election. Tally centers enter *result forms* and @@ -83,7 +83,7 @@ form is not a replacement form it will have a pre-assigned center and station. If it is a replacement form the clerk will next enter the center and station number for that form. -![](images/intake.png) +![Intake](images/intake.png) The original form or the replacement form is a duplicate if there is already a form in the system with the same ballot, center, and station. If the form is a @@ -115,17 +115,17 @@ The Data Entry Clerk begins by entering the barcode for a form they have received. The system then shows the results component, which displays the candidates that appear on the ballot assigned to the form in the order in which they appear on the ballot (and form). The physical form -shows the number of results for each candidate. The Data Entry Clerk must enter the +shows the number of results for each candidate. The Data Entry Clerk must enter the results for each candidate that appear on the form into the system. -![](images/data-entry-results.png) +![Data entry results](images/data-entry-results.png) If the form contains a reconcilliation component, the Clerk must also enter reconciliation information regarding the number of ballots, number of stampted votes, number of damaged votes, etc. The system will use this information later when deciding whether to Audit the form. -![](images/data-entry.png) +![data entry](images/data-entry.png) After a Data Entry 1 Clerk has entered the form's information, the system moves the form to Data Entry 2 and the Clerk is instructed to move the physical form to @@ -141,9 +141,9 @@ and 2 matches exactly the Clerk is simply displayed a message and the form is moved to quality control. The Corrections Clerk begins by entering a barcode for the form they have received. -![](images/corrections.png) +![corrections](images/corrections.png) -If any entries do not match, the system displays the information entered +If any entries do not match, the system displays the information entered during Data Entry 1 and 2 side-by-side. The Corrections Clerk must select the correct information and select "Submit Corrections", "Not Correct - Reject", or "Abort". If the Clerk chooses "Submit" the corrections are stored and the form @@ -154,7 +154,7 @@ in the pipeline from there. If the Clerk choose "Abort" no corrections are saved, the forms stays in corrections, and the Clerk is returned to the enter barcode entry screen. -![](images/corrections-results.png) +![corrections results](images/corrections-results.png) ### Quality Control @@ -169,7 +169,7 @@ form is move to Data Entry 1 and continues in the pipeline from there. If the Clerk chooses "Abort" the form stays in quality control, and the Clerk is returned to the enter barcode screen. -![](images/quality-control.png) +![quality control](images/quality-control.png) ### Archiving @@ -187,7 +187,7 @@ Otherwise, the Archiving Clerk prints an *Archived* cover sheet and attaches it to the form. The Clerk selects "Print Successful", moving the form to archived state. The form is then filed away. -![](images/archive.png) +![archive](images/archive.png) ## Review Stages @@ -223,7 +223,7 @@ recommendations made by a clerk for the form the choose the system moves form to Unsubmitted state, and the physical form is returned to the Intake Section. -![](images/clearance.png) +![clearance](images/clearance.png) ### Audit @@ -260,7 +260,7 @@ recommending that the form skip Quarantine Checks on its next time through the system. The form is then hidden from the Audit team and must then be reviewed by a Super Administration before moving to Data Entry 1. -![](images/audit-review.png) +![audit review](images/audit-review.png) ## Super Administrator Views @@ -277,7 +277,7 @@ change. The *Forms Waiting For Approval* list shows those forms which a Super Administrator must review. Rows in this list link to the Audit review view for the forms. -![](images/admin-review.png) +![admin review](images/admin-review.png) ### Reports Views @@ -286,7 +286,7 @@ current state within the system. This view is useful to determine where a form is in processing and to see how many forms from a particular center have been entered into the system. -![](images/reports-progress.png) +![reports progress](images/reports-progress.png) The *Reports Offices* list shows the percentage of forms in each state within the system. This can be used to balance the Clerk assigned to specific @@ -294,7 +294,7 @@ stations. For example, if there are twice as many forms in Data Entry 1 than in Data Entry 2, it may be wise to assign some Clerk from Data Entry 2 to Dat Entry 1. -![](images/reports-race.png) +![reports race](images/reports-race.png) This view also shows the number of Intaken and Archived form for each Office (analogous to a type of voting district). This allows administrators to track @@ -319,12 +319,11 @@ that can be entered into the system. The *Forms Not Received List* shows the barcodes for forms that the system has not yet intaken. - ### Results Exports The *Downloads* section allows Super Administrators to export both simple and deteailed results views. The *All Candidate Votes* download shows a simple -spreadsheet of the leading candidates and their votes accross all ballots. The +spreadsheet of the leading candidates and their votes accross all ballots. The same for *Active Candidate Votes* which only shows votes for active candidates. The *Result Form List* returns a detailed file of the results entered for every result form and associated information about that result form, e.g. gender, diff --git a/locale/ar/LC_MESSAGES/django.mo b/locale/ar/LC_MESSAGES/django.mo index 79b760af3..3012a9924 100644 Binary files a/locale/ar/LC_MESSAGES/django.mo and b/locale/ar/LC_MESSAGES/django.mo differ diff --git a/locale/ar/LC_MESSAGES/django.po b/locale/ar/LC_MESSAGES/django.po index 3e465c2e5..73ade890f 100644 --- a/locale/ar/LC_MESSAGES/django.po +++ b/locale/ar/LC_MESSAGES/django.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: Libya Tally\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-03 07:49+0000\n" +"POT-Creation-Date: 2024-11-04 11:23+0300\n" "PO-Revision-Date: 2014-02-21 16:33+0000\n" "Last-Translator: MonaHago \n" "Language-Team: Arabic (http://www.transifex.com/projects/p/libya-tally/" @@ -59,8 +59,8 @@ msgstr "الرقم التسلسلي غير مطابق" msgid "Station disabled." msgstr "رمز المحطة :" -#: tally_ho/apps/tally/forms/barcode_form.py:101 -msgid "Race disabled." +#: tally_ho/apps/tally/forms/barcode_form.py:99 +msgid "Ballot disabled." msgstr "رمز المحطة :" #: tally_ho/apps/tally/forms/candidate_formset.py:14 @@ -76,8 +76,8 @@ msgstr "يقبل أرقام فقط في خانة رقم المركز" #: tally_ho/apps/tally/forms/center_details_form.py:33 #: tally_ho/apps/tally/forms/remove_center_form.py:32 #: tally_ho/apps/tally/forms/remove_station_form.py:32 -#: tally_ho/apps/tally/templates/data/centers.html:65 -#: tally_ho/apps/tally/templates/reports/station_progress.html:33 +#: tally_ho/apps/tally/templates/data/centers.html:66 +#: tally_ho/apps/tally/templates/reports/station_progress.html:34 #: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:21 #: tally_ho/apps/tally/templates/super_admin/form_action.html:13 #: tally_ho/apps/tally/templates/super_admin/form_audit.html:24 @@ -93,11 +93,10 @@ msgstr "نسخة من رقم المركز" #: tally_ho/apps/tally/forms/center_details_form.py:43 #: tally_ho/apps/tally/forms/remove_station_form.py:38 -#: tally_ho/apps/tally/templates/data/centers.html:66 -#: tally_ho/apps/tally/templates/data/forms.html:76 +#: tally_ho/apps/tally/templates/data/centers.html:67 +#: tally_ho/apps/tally/templates/data/forms.html:74 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:59 -#: tally_ho/apps/tally/templates/reports/form_results.html:69 -#: tally_ho/apps/tally/templates/reports/station_progress.html:34 +#: tally_ho/apps/tally/templates/reports/station_progress.html:35 #: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:22 #: tally_ho/apps/tally/templates/super_admin/form_action.html:14 #: tally_ho/apps/tally/templates/super_admin/form_audit.html:25 @@ -137,17 +136,17 @@ msgid "Race is disabled." msgstr "اسم المركز" #: tally_ho/apps/tally/forms/center_details_form.py:101 -#: tally_ho/apps/tally/forms/disable_entity_form.py:71 +#: tally_ho/apps/tally/forms/disable_entity_form.py:79 #: tally_ho/apps/tally/forms/remove_center_form.py:50 #: tally_ho/apps/tally/forms/remove_center_form.py:64 #: tally_ho/apps/tally/forms/remove_station_form.py:63 -#: tally_ho/libs/utils/active_status.py:37 +#: tally_ho/libs/utils/active_status.py:38 msgid "Center Number does not exist" msgstr "أرقام المركز غير متطابقة" #: tally_ho/apps/tally/forms/center_details_form.py:104 -#: tally_ho/apps/tally/forms/disable_entity_form.py:74 -#: tally_ho/libs/utils/active_status.py:39 +#: tally_ho/apps/tally/forms/disable_entity_form.py:82 +#: tally_ho/libs/utils/active_status.py:40 msgid "Station Number does not exist" msgstr "أرقام المركز غير متطابقة" @@ -161,8 +160,8 @@ msgstr "بعض الحقول فارغة!" #: tally_ho/apps/tally/forms/create_result_form.py:61 #: tally_ho/apps/tally/forms/edit_result_form.py:48 -msgid "Race for ballot is disabled" -msgstr "اسم المركز" +msgid "Ballot is disabled" +msgstr "رمز المحطة :" #: tally_ho/apps/tally/forms/create_result_form.py:64 #: tally_ho/apps/tally/forms/edit_result_form.py:51 @@ -183,8 +182,8 @@ msgid "" "exists" msgstr "يوجد بالفعل نموذج نتيجة بالمركز المختار ورقم المحطة وبطاقة الاقتراع" -#: tally_ho/apps/tally/forms/create_result_form.py:93 -#: tally_ho/apps/tally/forms/edit_result_form.py:56 +#: tally_ho/apps/tally/forms/create_result_form.py:96 +#: tally_ho/apps/tally/forms/edit_result_form.py:60 msgid "Ballot number do not match for center and station" msgstr "محطة لا يجدي موجودة لمركز المختار" @@ -192,29 +191,35 @@ msgstr "محطة لا يجدي موجودة لمركز المختار" msgid "Center field is required" msgstr "اسم المركز" -#: tally_ho/apps/tally/forms/disable_entity_form.py:17 +#: tally_ho/apps/tally/forms/disable_entity_form.py:19 msgid "Expecting one option selected" msgstr "تتوقع خيار واحد مختارة" -#: tally_ho/apps/tally/forms/disable_entity_form.py:20 +#: tally_ho/apps/tally/forms/disable_entity_form.py:22 msgid "Select a reason" msgstr "اختر سببا" -#: tally_ho/apps/tally/forms/disable_entity_form.py:27 +#: tally_ho/apps/tally/forms/disable_entity_form.py:29 msgid "Comments" msgstr "تعليق الفريق:" -#: tally_ho/apps/tally/forms/disable_entity_form.py:79 -#: tally_ho/apps/tally/forms/edit_race_form.py:54 -#: tally_ho/libs/utils/active_status.py:72 -msgid "Race does not exist" +#: tally_ho/apps/tally/forms/disable_entity_form.py:87 +#: tally_ho/apps/tally/forms/edit_ballot_form.py:50 +#: tally_ho/libs/utils/active_status.py:73 +msgid "Ballot does not exist" +msgstr "الرقم التسلسلي غير مطابق" + +#: tally_ho/apps/tally/forms/disable_entity_form.py:93 +#: tally_ho/libs/utils/active_status.py:97 +msgid "Electrol Race does not exist" msgstr "الرقم التسلسلي غير مطابق" -#: tally_ho/apps/tally/forms/disable_entity_form.py:81 +#: tally_ho/apps/tally/forms/disable_entity_form.py:95 msgid "Error" msgstr "خطأ" -#: tally_ho/apps/tally/forms/edit_race_form.py:26 +#: tally_ho/apps/tally/forms/edit_ballot_form.py:26 +#: tally_ho/apps/tally/forms/edit_electrol_race_form.py:31 msgid "Add new comments" msgstr "أضف تعليقات جديدة" @@ -237,8 +242,6 @@ msgid "File extension ({file_extension}) is not supported." msgstr "" #: tally_ho/apps/tally/forms/pass_to_quality_control_form.py:20 -#, fuzzy, python-brace-format -#| msgid "Result Form with key {pk} does not exit." msgid "Result Form with key {pk} does not exit." msgstr "الاستمارة %s غير موجودة" @@ -255,6 +258,10 @@ msgstr "كلمة مرور جديدة" msgid "New password confirmation" msgstr "تأكيد كلمة المرور الجديدة" +#: tally_ho/apps/tally/forms/recon_form.py:75 +msgid "Total of fied 5 and 7 is incorrect" +msgstr "" + #: tally_ho/apps/tally/forms/site_info_form.py:21 msgid "User idle timeout" msgstr "مهلة خمول المستخدم" @@ -263,14 +270,6 @@ msgstr "مهلة خمول المستخدم" msgid "Administrators" msgstr "المسؤولين" -#: tally_ho/apps/tally/management/commands/check_barcodes.py:17 -msgid "" -"Check that the centers and stations assigned to result forms in the system " -"match those in the raw data." -msgstr "" -"تأكد من أن المراكز والمحطات المخصصة لينتج أشكال في النظام, تطابق تلك " -"الموجودة في البيانات الخام." - #: tally_ho/apps/tally/management/commands/create_demo_users.py:53 #: tally_ho/apps/tally/management/commands/create_quarantine_checks.py:7 msgid "Create demo users with roles/groups." @@ -288,15 +287,17 @@ msgstr "تكوين مجموعات" msgid "Export candidate votes list." msgstr "قائمة أصوات مرشح التصدير." -#: tally_ho/apps/tally/management/commands/import_data.py:519 -#: tally_ho/apps/tally/management/commands/import_resultforms.py:14 -msgid "Import polling data." -msgstr "احضار بيانات الاقتراع" - #: tally_ho/apps/tally/management/commands/import_staff_list.py:105 msgid "Import staff list." msgstr "تصدير قائمة العاملين" +#: tally_ho/apps/tally/management/commands/utils.py:217 +#, python-brace-format +msgid "Column {str_m_cols} is missing in the {file_name} file" +msgid_plural "Columns {str_m_cols} are missing in the {file_name} file" +msgstr[0] "مفقود في ملف المراكز %(cols)s عمود" +msgstr[1] "مفقودة في ملف المراكز %(cols)s الأعمدة" + #: tally_ho/apps/tally/models/audit.py:58 msgid "Blank Reconcilliation" msgstr "استمارة التسوية فارغة" @@ -319,40 +320,11 @@ msgstr "ارقام غير واضحة" msgid "Other" msgstr "اخري" -#: tally_ho/apps/tally/models/ballot.py:38 -#: tally_ho/apps/tally/models/sub_constituency.py:55 +#: tally_ho/apps/tally/models/ballot.py:40 msgid "General and Component" msgstr "تنافس عام ومكون" -#: tally_ho/apps/tally/models/candidate.py:38 -#: tally_ho/apps/tally/models/sub_constituency.py:57 -msgid "General" -msgstr "عام" - -#: tally_ho/apps/tally/models/candidate.py:39 -#: tally_ho/apps/tally/models/sub_constituency.py:52 -msgid "Women" -msgstr "نساء" - -#: tally_ho/apps/tally/models/candidate.py:40 -msgid "Component Amazigh" -msgstr "مكون الأمازيغ" - -#: tally_ho/apps/tally/models/candidate.py:41 -msgid "Component Twarag" -msgstr "مكون الطوارق" - -#: tally_ho/apps/tally/models/candidate.py:42 -msgid "Component Tebu" -msgstr "مكون التبو" - -#: tally_ho/apps/tally/models/candidate.py:43 -#: tally_ho/apps/tally/models/sub_constituency.py:59 -msgid "Presidential" -msgstr "رئاسي" - #: tally_ho/apps/tally/models/center.py:67 -#: tally_ho/apps/tally/models/result_form.py:461 msgid "Special" msgstr "خاص" @@ -380,159 +352,146 @@ msgstr "رقم المركز موجود فى النظام مسبقاً" msgid "Form Incorrectly Entered into the System" msgstr "تم ادخال الاستمارة بشكل غير صحيح فى النظام" -#: tally_ho/apps/tally/models/quality_control.py:39 +#: tally_ho/apps/tally/models/quality_control.py:38 msgid "Reviews Completed" msgstr "استعراض مكتمل" -#: tally_ho/apps/tally/models/quality_control.py:40 +#: tally_ho/apps/tally/models/quality_control.py:39 msgid "Reviews Required" msgstr "استعراض المطلوبة" -#: tally_ho/apps/tally/models/reconciliation_form.py:48 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:30 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:13 +#: tally_ho/apps/tally/models/reconciliation_form.py:49 msgid "from:" msgstr " من:" -#: tally_ho/apps/tally/models/reconciliation_form.py:49 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:32 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:15 +#: tally_ho/apps/tally/models/reconciliation_form.py:50 msgid "to:" msgstr "إلى:" -#: tally_ho/apps/tally/models/reconciliation_form.py:50 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:104 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:87 +#: tally_ho/apps/tally/models/reconciliation_form.py:51 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:126 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:115 msgid "Is the form stamped?" msgstr "هل الاستمارة مختومة ؟" -#: tally_ho/apps/tally/models/reconciliation_form.py:52 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:43 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:26 +#: tally_ho/apps/tally/models/reconciliation_form.py:53 msgid "Total number of ballots received by the polling station" msgstr "العدد الكلي لاوراق الاقتراع التي استلمتها محطة الاقتراع" -#: tally_ho/apps/tally/models/reconciliation_form.py:54 -msgid "Number of signatures in the VR" -msgstr "عدد التواقيع فى سجل الناخبين" +#: tally_ho/apps/tally/models/reconciliation_form.py:55 +msgid "Number of voter cards in the ballot box" +msgstr "عدد بطاقات الناخبين داخل الصندوق 2" -#: tally_ho/apps/tally/models/reconciliation_form.py:56 +#: tally_ho/apps/tally/models/reconciliation_form.py:57 msgid "Number of unused ballots" msgstr "عدد أوراق الاقتراع الغير مستخدمة" -#: tally_ho/apps/tally/models/reconciliation_form.py:58 +#: tally_ho/apps/tally/models/reconciliation_form.py:59 msgid "Number of spoiled ballots" msgstr "عدد أوراق الاقتراع التالفة" -#: tally_ho/apps/tally/models/reconciliation_form.py:60 +#: tally_ho/apps/tally/models/reconciliation_form.py:61 msgid "Number of cancelled ballots" msgstr "عدد أوراق الاقتراع الملغاة" -#: tally_ho/apps/tally/models/reconciliation_form.py:62 +#: tally_ho/apps/tally/models/reconciliation_form.py:63 msgid "Total number of ballots remaining outside the ballot box" msgstr "العدد الكلي لأوراق الاقتراع خارج صندوق الاقتراع" -#: tally_ho/apps/tally/models/reconciliation_form.py:64 +#: tally_ho/apps/tally/models/reconciliation_form.py:65 msgid "Number of ballots found inside the ballot box" msgstr "عدد أوراق الاقتراع الموجودة داخل صندوق الاقتراع" -#: tally_ho/apps/tally/models/reconciliation_form.py:66 +#: tally_ho/apps/tally/models/reconciliation_form.py:67 msgid "Total number of ballots found inside and outside the ballot box" msgstr "مجموع أوراق الاقتراع الموجودة داخل و خارج صندوق الاقتراع" -#: tally_ho/apps/tally/models/reconciliation_form.py:68 +#: tally_ho/apps/tally/models/reconciliation_form.py:69 +msgid "Total of fields 5+7" +msgstr "الاصوات" + +#: tally_ho/apps/tally/models/reconciliation_form.py:71 msgid "Number of unstamped ballots" msgstr "عدد الأوراق الغير مختومة" -#: tally_ho/apps/tally/models/reconciliation_form.py:70 -msgid "Number of blank ballots" -msgstr "عدد أوراق الاقتراع الغير مستخدمة" - -#: tally_ho/apps/tally/models/reconciliation_form.py:72 +#: tally_ho/apps/tally/models/reconciliation_form.py:73 msgid "Number of invalid votes" msgstr "عدد الأوراق الصحيحة" -#: tally_ho/apps/tally/models/reconciliation_form.py:74 +#: tally_ho/apps/tally/models/reconciliation_form.py:75 msgid "Number of valid votes" msgstr "عدد الأوراق الصحيحة" -#: tally_ho/apps/tally/models/reconciliation_form.py:76 +#: tally_ho/apps/tally/models/reconciliation_form.py:77 msgid "Total number of the sorted and counted ballots" msgstr "مجموع أوراق الاقتراع المفروزة والمعدودة" -#: tally_ho/apps/tally/models/reconciliation_form.py:78 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:96 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:79 +#: tally_ho/apps/tally/models/reconciliation_form.py:79 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:118 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:107 msgid "Is the form signed by polling officer 1?" msgstr "هل تم توقيع النوذج من قبل موظف الاقتراع 1؟" -#: tally_ho/apps/tally/models/reconciliation_form.py:80 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:98 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:81 +#: tally_ho/apps/tally/models/reconciliation_form.py:81 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:120 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:109 msgid "Is the form signed by polling officer 2?" msgstr "هل تم توقيع النوذج من قبل موظف الاقتراع 2؟" -#: tally_ho/apps/tally/models/reconciliation_form.py:82 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:100 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:83 +#: tally_ho/apps/tally/models/reconciliation_form.py:83 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:122 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:111 msgid "Is the form signed by the polling station chair?" msgstr "هل تم توقيع النوذج من قبل المسؤول عن محطة الاقتراع ؟" -#: tally_ho/apps/tally/models/reconciliation_form.py:83 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:102 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:85 +#: tally_ho/apps/tally/models/reconciliation_form.py:84 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:124 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:113 msgid "Is the form dated?" msgstr "هل النموذج يحتوى تاريخ؟" -#: tally_ho/apps/tally/models/result_form.py:22 -#: tally_ho/libs/models/enums/gender.py:12 +#: tally_ho/apps/tally/models/result_form.py:20 msgid "Male" msgstr "ذكر" -#: tally_ho/apps/tally/models/result_form.py:23 -#: tally_ho/libs/models/enums/gender.py:13 +#: tally_ho/apps/tally/models/result_form.py:21 msgid "Female" msgstr "انثي" -#: tally_ho/apps/tally/models/result_form.py:53 -#: tally_ho/libs/views/corrections.py:69 +#: tally_ho/apps/tally/models/result_form.py:51 +#: tally_ho/libs/views/corrections.py:68 msgid "Result Form has no double entries." msgstr "نتيجة الاستمارة لا توجد به ادخال مزدوج" -#: tally_ho/apps/tally/models/result_form.py:59 -#, python-format +#: tally_ho/apps/tally/models/result_form.py:57 msgid "" "Unexpected number of results in form %(barcode)s, return result form to Data " "Entry 1." msgstr "" -"عدد غير متوقع من النتائج في النموذج %(barcode)s, ارجع نموذج النتائج الي " -"الادخال 1. " +"عدد غير متوقع من النتائج في النموذج {barcode}s، يرجى إرجاع نموذج النتائج إلى " +"إدخال البيانات 1." -#: tally_ho/apps/tally/models/result_form.py:99 +#: tally_ho/apps/tally/models/result_form.py:97 msgid "Votes do not match!" msgstr "الرقم التسلسلي غير مطابق" -#: tally_ho/apps/tally/models/result_form.py:126 +#: tally_ho/apps/tally/models/result_form.py:124 msgid "Unexpected number of reconciliation forms" msgstr "عدد غير متوقع من استمارات التسوية" -#: tally_ho/apps/tally/models/result_form.py:252 -#: tally_ho/apps/tally/models/result_form.py:257 -#: tally_ho/apps/tally/models/result_form.py:393 -#: tally_ho/apps/tally/models/result_form.py:399 -#: tally_ho/apps/tally/templates/corrections/comparison.html:16 +#: tally_ho/apps/tally/models/result_form.py:232 +#: tally_ho/apps/tally/models/result_form.py:237 +#: tally_ho/apps/tally/models/result_form.py:371 +#: tally_ho/apps/tally/models/result_form.py:377 +#: tally_ho/apps/tally/templates/corrections/comparison.html:15 #: tally_ho/apps/tally/templates/corrections/required.html:29 msgid "No" msgstr "لا " -#: tally_ho/apps/tally/models/result_form.py:286 +#: tally_ho/apps/tally/models/result_form.py:270 msgid "Corrections Required!" msgstr "التصحيحات المطلوبة!" -#: tally_ho/apps/tally/models/sub_constituency.py:61 -msgid "Undefined" -msgstr "غير معروف" - #: tally_ho/apps/tally/templates/audit/dashboard.html:7 msgid "Audit List" msgstr "قائمة المراجعة" @@ -551,9 +510,8 @@ msgstr "تسجيل أستمارة جديدة " #: tally_ho/apps/tally/templates/barcode_verify.html:19 #: tally_ho/apps/tally/templates/barcode_verify.html:25 #: tally_ho/apps/tally/templates/clearance/dashboard.html:46 -#: tally_ho/apps/tally/templates/data/forms.html:73 +#: tally_ho/apps/tally/templates/data/forms.html:71 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:57 -#: tally_ho/apps/tally/templates/reports/form_results.html:71 #: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:20 #: tally_ho/apps/tally/templates/super_admin/form_action.html:12 #: tally_ho/apps/tally/templates/super_admin/form_audit.html:23 @@ -566,11 +524,11 @@ msgstr "رقم التسلسل" #: tally_ho/apps/tally/templates/audit/dashboard.html:29 #: tally_ho/apps/tally/templates/clearance/dashboard.html:47 -#: tally_ho/apps/tally/templates/data/centers.html:64 -#: tally_ho/apps/tally/templates/reports/station_progress.html:32 +#: tally_ho/apps/tally/templates/data/centers.html:65 +#: tally_ho/apps/tally/templates/reports/station_progress.html:33 #: tally_ho/apps/tally/templates/super_admin/form_results_duplicates.html:15 msgid "Center Name" -msgstr "اسم المركز" +msgstr "اسم المركز الانتخابي" #: tally_ho/apps/tally/templates/audit/dashboard.html:30 #: tally_ho/apps/tally/templates/clearance/dashboard.html:48 @@ -609,13 +567,12 @@ msgstr "الموقوف" #: tally_ho/apps/tally/templates/audit/dashboard.html:55 #: tally_ho/apps/tally/templates/clearance/dashboard.html:54 #: tally_ho/apps/tally/templates/clearance/dashboard.html:70 -#: tally_ho/apps/tally/templates/super_admin/form_action.html:36 +#: tally_ho/apps/tally/templates/super_admin/form_action.html:38 msgid "Review" msgstr "المراجعة" #: tally_ho/apps/tally/templates/audit/dashboard.html:66 #: tally_ho/apps/tally/templates/clearance/dashboard.html:82 -#: tally_ho/apps/tally/templates/data/races.html:66 #: tally_ho/apps/tally/templates/super_admin/form_results_duplicates.html:44 #: tally_ho/apps/tally/templates/super_admin/quarantine_checks_list.html:45 msgid "previous" @@ -631,10 +588,12 @@ msgid "" " Page %(number)s of %(num_pages)s.\n" " " msgstr "" +"\n" +" الصفحة %(number)s من %(num_pages)s.\n" +" " #: tally_ho/apps/tally/templates/audit/dashboard.html:76 #: tally_ho/apps/tally/templates/clearance/dashboard.html:92 -#: tally_ho/apps/tally/templates/data/races.html:74 #: tally_ho/apps/tally/templates/super_admin/form_results_duplicates.html:56 #: tally_ho/apps/tally/templates/super_admin/quarantine_checks_list.html:55 msgid "next" @@ -649,7 +608,7 @@ msgstr "حالة التدقيق : رقم الفريق" #: tally_ho/apps/tally/templates/clearance/print_cover.html:11 #: tally_ho/apps/tally/templates/print_cover_common.html:4 msgid "Center Name:" -msgstr "اسم المركز :" +msgstr "اسم المركز الانتخابي :" #: tally_ho/apps/tally/templates/audit/print_cover.html:12 #: tally_ho/apps/tally/templates/clearance/print_cover.html:12 @@ -660,97 +619,107 @@ msgstr "الرقم التسلسلي" #: tally_ho/apps/tally/templates/center_details.html:21 #: tally_ho/apps/tally/templates/clearance/print_cover.html:15 msgid "Office Name:" -msgstr "اسم المكتب :" +msgstr "مكتب الإدارة الانتخابية :" #: tally_ho/apps/tally/templates/audit/print_cover.html:16 +#: tally_ho/apps/tally/templates/center_details.html:29 #: tally_ho/apps/tally/templates/clearance/print_cover.html:16 #: tally_ho/apps/tally/templates/print_cover_common.html:13 -msgid "Form Type:" -msgstr "نوع الاستمارة :" +#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:36 +msgid "Election Level:" +msgstr "نوع الانتخابات:" -#: tally_ho/apps/tally/templates/audit/print_cover.html:19 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:19 -msgid "Sub-Constituency:" -msgstr "الدائرة الفرعية :" +#: tally_ho/apps/tally/templates/audit/print_cover.html:17 +#: tally_ho/apps/tally/templates/center_details.html:33 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:17 +#: tally_ho/apps/tally/templates/print_cover_common.html:16 +#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:40 +msgid "Sub Race Type:" +msgstr "نوع التنافس" #: tally_ho/apps/tally/templates/audit/print_cover.html:20 -#: tally_ho/apps/tally/templates/center_details.html:43 #: tally_ho/apps/tally/templates/clearance/print_cover.html:20 +msgid "Sub-Constituency:" +msgstr "الدائرة الفرعية :" + +#: tally_ho/apps/tally/templates/audit/print_cover.html:21 +#: tally_ho/apps/tally/templates/center_details.html:47 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:21 #: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:16 msgid "Center Code:" -msgstr "رمز المركز :" +msgstr "رقم المركز الانتخابي :" -#: tally_ho/apps/tally/templates/audit/print_cover.html:23 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:23 +#: tally_ho/apps/tally/templates/audit/print_cover.html:24 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:24 #: tally_ho/apps/tally/templates/quality_control/print_cover.html:33 msgid "Number of Registrants:" msgstr "عدد المسجلين:" -#: tally_ho/apps/tally/templates/audit/print_cover.html:24 -#: tally_ho/apps/tally/templates/center_details.html:47 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:28 +#: tally_ho/apps/tally/templates/audit/print_cover.html:25 +#: tally_ho/apps/tally/templates/center_details.html:51 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:29 msgid "Station Code:" -msgstr "رمز المحطة :" +msgstr "رقم محطة الإقتراع:" -#: tally_ho/apps/tally/templates/audit/print_cover.html:28 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:32 +#: tally_ho/apps/tally/templates/audit/print_cover.html:29 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:33 msgid "Date Created:" msgstr "تاريخ الانشاء" -#: tally_ho/apps/tally/templates/audit/print_cover.html:31 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:39 +#: tally_ho/apps/tally/templates/audit/print_cover.html:32 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:40 msgid "Date Team Modified:" msgstr "تاريخ تعديل الفريق :" -#: tally_ho/apps/tally/templates/audit/print_cover.html:34 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:42 +#: tally_ho/apps/tally/templates/audit/print_cover.html:35 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:43 msgid "Date Supervisor Modified:" msgstr "تاريخ تعديل المشرف:" -#: tally_ho/apps/tally/templates/audit/print_cover.html:37 +#: tally_ho/apps/tally/templates/audit/print_cover.html:38 #: tally_ho/apps/tally/templates/audit/review.html:25 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:49 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:50 #: tally_ho/apps/tally/templates/clearance/review.html:16 msgid "Problem" msgstr "مشكله " -#: tally_ho/apps/tally/templates/audit/print_cover.html:55 +#: tally_ho/apps/tally/templates/audit/print_cover.html:56 #: tally_ho/apps/tally/templates/audit/review.html:31 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:67 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:68 #: tally_ho/apps/tally/templates/clearance/review.html:32 msgid "Other:" msgstr "أخري" -#: tally_ho/apps/tally/templates/audit/print_cover.html:60 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:72 +#: tally_ho/apps/tally/templates/audit/print_cover.html:61 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:73 msgid "Action Conducted" msgstr "اجراء التصرف" -#: tally_ho/apps/tally/templates/audit/print_cover.html:66 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:78 +#: tally_ho/apps/tally/templates/audit/print_cover.html:67 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:79 msgid "Resolution Recommendation" msgstr "الحلول الموصى بها" -#: tally_ho/apps/tally/templates/audit/print_cover.html:70 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:82 +#: tally_ho/apps/tally/templates/audit/print_cover.html:71 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:83 msgid "Team Comment:" msgstr "تعليق الفريق:" -#: tally_ho/apps/tally/templates/audit/print_cover.html:73 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:85 +#: tally_ho/apps/tally/templates/audit/print_cover.html:74 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:86 msgid "Supervisor Comment:" msgstr "تعليق المشرف :" -#: tally_ho/apps/tally/templates/audit/print_cover.html:81 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:94 +#: tally_ho/apps/tally/templates/audit/print_cover.html:82 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:95 #: tally_ho/apps/tally/templates/intake/print_cover.html:62 #: tally_ho/apps/tally/templates/quality_control/print_cover.html:52 msgid "Print" msgstr "طباعة" -#: tally_ho/apps/tally/templates/audit/print_cover.html:82 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:96 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:98 +#: tally_ho/apps/tally/templates/audit/print_cover.html:83 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:97 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:99 #: tally_ho/apps/tally/templates/intake/print_cover.html:64 #: tally_ho/apps/tally/templates/intake/print_cover.html:66 #: tally_ho/apps/tally/templates/quality_control/print_cover.html:53 @@ -789,34 +758,35 @@ msgstr "نموذج معطل:" #: tally_ho/apps/tally/templates/audit/review.html:32 #: tally_ho/apps/tally/templates/clearance/review.html:33 -msgid "Action Prior to Recommendation" -msgstr "الحلول الموصى بها" - -#: tally_ho/apps/tally/templates/audit/review.html:33 -#: tally_ho/apps/tally/templates/clearance/review.html:34 -msgid "Action prior to recommendation:" -msgstr "الحلول الموصى بها" - -#: tally_ho/apps/tally/templates/audit/review.html:36 -#: tally_ho/apps/tally/templates/clearance/review.html:37 -msgid "Resolution recommendation:" -msgstr "الحلول الموصى بها" - -#: tally_ho/apps/tally/templates/audit/review.html:39 -#: tally_ho/apps/tally/templates/clearance/review.html:40 +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:42 #: tally_ho/apps/tally/templates/super_admin/edit_center.html:41 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:42 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:42 #: tally_ho/apps/tally/templates/super_admin/edit_result_form.html:36 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:49 #: tally_ho/apps/tally/templates/super_admin/form.html:22 -#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:62 +#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:48 #: tally_ho/apps/tally/templates/tally_manager/edit_user_profile.html:26 #: tally_ho/apps/tally/templates/tally_manager/set_user_timeout.html:21 -#: tally_ho/apps/tally/templates/tally_manager/tally_files_form.html:69 +#: tally_ho/apps/tally/templates/tally_manager/tally_files_form.html:83 #: tally_ho/apps/tally/templates/tally_manager/tally_form.html:44 msgid "Save" msgstr "حفظ" +#: tally_ho/apps/tally/templates/audit/review.html:33 +#: tally_ho/apps/tally/templates/clearance/review.html:34 +msgid "Action Prior to Recommendation" +msgstr "الحلول الموصى بها" + +#: tally_ho/apps/tally/templates/audit/review.html:34 +#: tally_ho/apps/tally/templates/clearance/review.html:35 +msgid "Action prior to recommendation:" +msgstr "الحلول الموصى بها" + +#: tally_ho/apps/tally/templates/audit/review.html:37 +#: tally_ho/apps/tally/templates/clearance/review.html:38 +msgid "Resolution recommendation:" +msgstr "الحلول الموصى بها" + #: tally_ho/apps/tally/templates/audit/review.html:41 #: tally_ho/apps/tally/templates/clearance/review.html:42 msgid "Forward to Supervisor and Print Cover" @@ -852,7 +822,7 @@ msgid "Barcode Copy" msgstr "نسخة من الرقم التسلسلي" #: tally_ho/apps/tally/templates/barcode_verify.html:40 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:215 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:233 #: tally_ho/apps/tally/templates/enter_center_details.html:48 #: tally_ho/apps/tally/templates/super_admin/remove_center.html:27 #: tally_ho/apps/tally/templates/super_admin/remove_station.html:33 @@ -882,16 +852,12 @@ msgstr "الرقم التسلسل :" #: tally_ho/apps/tally/templates/center_details.html:17 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:22 msgid "Region Name:" -msgstr "اسم المركز :" - -#: tally_ho/apps/tally/templates/center_details.html:29 -msgid "Form Race Type:" -msgstr "نوع استمارة التنافس" +msgstr "المنطقة :" -#: tally_ho/apps/tally/templates/center_details.html:36 +#: tally_ho/apps/tally/templates/center_details.html:40 #: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:32 msgid "Voting District:" -msgstr "دائرة الاقتراع :" +msgstr "الدائرة الانتخابية :" #: tally_ho/apps/tally/templates/check_center_details.html:9 #: tally_ho/apps/tally/templates/check_clearance_center_details.html:9 @@ -901,7 +867,7 @@ msgstr "التأكد من بيانات المركز بالمقارنة مع ال #: tally_ho/apps/tally/templates/check_center_details.html:16 #: tally_ho/apps/tally/templates/check_clearance_center_details.html:15 #: tally_ho/apps/tally/templates/super_admin/remove_center_confirmation.html:31 -#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:45 +#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:49 #: tally_ho/apps/tally/templates/super_admin/remove_station_confirmation.html:31 #: tally_ho/apps/tally/templates/tally_manager/remove_user_confirmation.html:39 #: tally_ho/apps/tally/templates/tally_manager/tally_remove.html:33 @@ -917,22 +883,23 @@ msgid "Name Does Not Match and Code Correct - Send to Clearance" msgstr "الاسم غير مطابق والرقم صحيح- ارسل الاستمارة الى التصحيح" #: tally_ho/apps/tally/templates/check_clearance_center_details.html:16 -#: tally_ho/apps/tally/templates/corrections/required.html:82 -#: tally_ho/apps/tally/templates/quality_control/dashboard.html:37 +#: tally_ho/apps/tally/templates/corrections/required.html:70 +#: tally_ho/apps/tally/templates/quality_control/dashboard.html:24 +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:45 #: tally_ho/apps/tally/templates/super_admin/edit_center.html:44 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:45 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:45 #: tally_ho/apps/tally/templates/super_admin/edit_result_form.html:39 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:52 #: tally_ho/apps/tally/templates/super_admin/form.html:25 -#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:65 +#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:51 #: tally_ho/apps/tally/templates/super_admin/remove_center_confirmation.html:32 -#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:46 +#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:50 #: tally_ho/apps/tally/templates/super_admin/remove_station_confirmation.html:32 #: tally_ho/apps/tally/templates/tally_manager/edit_user_profile.html:29 #: tally_ho/apps/tally/templates/tally_manager/remove_user_confirmation.html:41 #: tally_ho/apps/tally/templates/tally_manager/remove_user_confirmation.html:43 #: tally_ho/apps/tally/templates/tally_manager/set_user_timeout.html:24 -#: tally_ho/apps/tally/templates/tally_manager/tally_files_form.html:72 +#: tally_ho/apps/tally/templates/tally_manager/tally_files_form.html:86 #: tally_ho/apps/tally/templates/tally_manager/tally_form.html:48 #: tally_ho/apps/tally/templates/tally_manager/tally_form.html:58 #: tally_ho/apps/tally/templates/tally_manager/tally_remove.html:36 @@ -948,7 +915,7 @@ msgid "New Clearance" msgstr "تحقق" #: tally_ho/apps/tally/templates/clearance/dashboard.html:52 -#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:26 +#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:27 #: tally_ho/apps/tally/templates/super_admin/form_results_duplicates.html:19 msgid "Modified" msgstr "تعديل" @@ -961,10 +928,9 @@ msgstr "حالة النمودج" msgid "Clearance Case: Team Page" msgstr "حالة التخليص: فريق الصفحة" -#: tally_ho/apps/tally/templates/clearance/print_cover.html:26 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:35 -#: tally_ho/apps/tally/templates/clearance/print_cover.html:45 -#: tally_ho/apps/tally/templates/data/races.html:45 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:27 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:36 +#: tally_ho/apps/tally/templates/clearance/print_cover.html:46 #: tally_ho/apps/tally/templates/super_admin/form_results_duplicates.html:36 msgid "None" msgstr "لا أحد" @@ -1002,7 +968,7 @@ msgid "Return to Clearance Team" msgstr "رجوع الى فريق التصحيح" #: tally_ho/apps/tally/templates/corrections/comparison.html:2 -msgid "Results Form Corrections" +msgid "Results Form Corrections:" msgstr "نتائج استمارات التصحيح" #: tally_ho/apps/tally/templates/corrections/comparison.html:5 @@ -1010,37 +976,38 @@ msgstr "نتائج استمارات التصحيح" msgid "Corrections Required?" msgstr "هل التصحيحات المطلوبة؟" -#: tally_ho/apps/tally/templates/corrections/comparison.html:7 +#: tally_ho/apps/tally/templates/corrections/comparison.html:6 #: tally_ho/apps/tally/templates/corrections/required.html:21 msgid "Entry 1" msgstr "مدخل 1" -#: tally_ho/apps/tally/templates/corrections/comparison.html:8 +#: tally_ho/apps/tally/templates/corrections/comparison.html:7 #: tally_ho/apps/tally/templates/corrections/required.html:22 msgid "Entry 2" msgstr "مدخل 2" -#: tally_ho/apps/tally/templates/corrections/comparison.html:9 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:191 +#: tally_ho/apps/tally/templates/corrections/comparison.html:8 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:214 #: tally_ho/apps/tally/templates/quality_control/results.html:7 msgid "Candidate" msgstr "مرشح" -#: tally_ho/apps/tally/templates/corrections/comparison.html:10 -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:190 +#: tally_ho/apps/tally/templates/corrections/comparison.html:9 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:213 #: tally_ho/apps/tally/templates/quality_control/results.html:6 msgid "No." msgstr "العدد" -#: tally_ho/apps/tally/templates/corrections/comparison.html:17 +#: tally_ho/apps/tally/templates/corrections/comparison.html:16 #: tally_ho/apps/tally/templates/corrections/required.html:30 msgid "Yes" msgstr "نعم " #: tally_ho/apps/tally/templates/corrections/match.html:7 -#: tally_ho/apps/tally/views/corrections.py:241 -#: tally_ho/apps/tally/views/corrections.py:434 -#: tally_ho/apps/tally/views/data_entry.py:403 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:32 +#: tally_ho/apps/tally/views/corrections.py:228 +#: tally_ho/apps/tally/views/corrections.py:417 +#: tally_ho/apps/tally/views/data_entry.py:410 msgid "Corrections" msgstr "تصحيحات" @@ -1060,14 +1027,83 @@ msgstr "تصحيح استمارة التسوية" msgid "Label" msgstr "العنوان" -#: tally_ho/apps/tally/templates/corrections/required.html:80 +#: tally_ho/apps/tally/templates/corrections/required.html:68 msgid "Submit Corrections" msgstr "احفظ التصحيحات" -#: tally_ho/apps/tally/templates/corrections/required.html:81 +#: tally_ho/apps/tally/templates/corrections/required.html:69 msgid "Not Correct - Reject" msgstr "غير صحيحة - مرفوضة" +#: tally_ho/apps/tally/templates/data/ballots.html:31 +#: tally_ho/apps/tally/views/super_admin.py:1191 +msgid "New Ballot" +msgstr "الرقم التسلسلي" + +#: tally_ho/apps/tally/templates/data/ballots.html:39 +#: tally_ho/apps/tally/templates/data/offices.html:31 +#: tally_ho/apps/tally/templates/reports/offices.html:12 +msgid "Number" +msgstr "رقم " + +#: tally_ho/apps/tally/templates/data/ballots.html:40 +#: tally_ho/apps/tally/templates/data/electrol_races.html:41 +msgid "Active" +msgstr "نشيط:" + +#: tally_ho/apps/tally/templates/data/ballots.html:41 +#: tally_ho/apps/tally/templates/data/candidates.html:40 +#: tally_ho/apps/tally/templates/data/electrol_races.html:39 +#: tally_ho/apps/tally/templates/data/forms.html:80 +#: tally_ho/apps/tally/templates/data/sub_cons_list.html:37 +#: tally_ho/apps/tally/templates/reports/form_results.html:371 +#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:24 +#: tally_ho/apps/tally/templates/super_admin/form_action.html:15 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:28 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:28 +#: tally_ho/apps/tally/templates/super_admin/form_progress.html:29 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:25 +msgid "Election Level" +msgstr "نوع الانتخابات" + +#: tally_ho/apps/tally/templates/data/ballots.html:42 +#: tally_ho/apps/tally/templates/data/candidates.html:41 +#: tally_ho/apps/tally/templates/data/electrol_races.html:40 +#: tally_ho/apps/tally/templates/data/forms.html:81 +#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:25 +#: tally_ho/apps/tally/templates/super_admin/form_action.html:16 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:29 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:29 +#: tally_ho/apps/tally/templates/super_admin/form_progress.html:30 +msgid "Sub Race Type" +msgstr "نوع التنافس" + +#: tally_ho/apps/tally/templates/data/ballots.html:43 +#: tally_ho/apps/tally/templates/data/candidates.html:42 +#: tally_ho/apps/tally/templates/data/electrol_races.html:42 +#: tally_ho/apps/tally/templates/data/tallies.html:34 +msgid "Last Modification" +msgstr "التعديل الاخير" + +#: tally_ho/apps/tally/templates/data/ballots.html:44 +msgid "Available for release" +msgstr "جاهز للارشفة" + +#: tally_ho/apps/tally/templates/data/ballots.html:45 +#: tally_ho/apps/tally/templates/data/candidates.html:43 +#: tally_ho/apps/tally/templates/data/electrol_races.html:43 +#: tally_ho/apps/tally/templates/data/forms.html:85 +#: tally_ho/apps/tally/templates/data/tallies.html:36 +#: tally_ho/apps/tally/templates/data/users.html:64 +#: tally_ho/apps/tally/templates/reports/admin_areas_excluded_after_investigation.html:43 +#: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:43 +#: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:46 +#: tally_ho/apps/tally/templates/reports/progressive_report.html:46 +#: tally_ho/apps/tally/templates/reports/summary_report.html:47 +#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_list.html:25 +msgid "Actions" +msgstr "تصحيحات" + #: tally_ho/apps/tally/templates/data/candidates.html:35 #: tally_ho/apps/tally/templates/data/tallies.html:31 msgid "Id" @@ -1076,6 +1112,7 @@ msgstr "معرف" #: tally_ho/apps/tally/templates/data/candidates.html:36 #: tally_ho/apps/tally/templates/data/offices.html:30 #: tally_ho/apps/tally/templates/data/regions.html:30 +#: tally_ho/apps/tally/templates/data/sub_cons_list.html:36 #: tally_ho/apps/tally/templates/data/tallies.html:32 #: tally_ho/apps/tally/templates/data/users.html:56 #: tally_ho/apps/tally/templates/intake/print_cover.html:34 @@ -1085,74 +1122,39 @@ msgstr "معرف" #: tally_ho/apps/tally/templates/reports/admin_areas_excluded_after_investigation.html:39 #: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:39 #: tally_ho/apps/tally/templates/reports/corrections_statistics.html:11 +#: tally_ho/apps/tally/templates/reports/form_results.html:366 #: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:40 #: tally_ho/apps/tally/templates/reports/progressive_report.html:40 #: tally_ho/apps/tally/templates/reports/summary_report.html:40 -#: tally_ho/apps/tally/templates/reports/turnout_report.html:40 #: tally_ho/apps/tally/templates/super_admin/quarantine_checks_list.html:19 #: tally_ho/apps/tally/templates/tally_manager/tally_remove.html:26 msgid "Name" msgstr "الاسم " #: tally_ho/apps/tally/templates/data/candidates.html:37 +#: tally_ho/apps/tally/templates/reports/form_results.html:369 #: tally_ho/apps/tally/templates/super_admin/quarantine_checks_list.html:24 msgid "Status" msgstr "حالة النمودج" #: tally_ho/apps/tally/templates/data/candidates.html:38 -#: tally_ho/apps/tally/templates/reports/form_results.html:74 +#: tally_ho/apps/tally/templates/reports/form_results.html:374 msgid "Order" msgstr "النظام" #: tally_ho/apps/tally/templates/data/candidates.html:39 #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:61 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:55 -#: tally_ho/apps/tally/templates/reports/form_results.html:81 msgid "Ballot" msgstr "الرقم التسلسلي" -#: tally_ho/apps/tally/templates/data/candidates.html:40 -#: tally_ho/apps/tally/templates/data/forms.html:81 -#: tally_ho/apps/tally/templates/data/races.html:29 -#: tally_ho/apps/tally/templates/reports/form_results.html:72 -#: tally_ho/apps/tally/templates/reports/races.html:29 -#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:24 -#: tally_ho/apps/tally/templates/super_admin/form_action.html:15 -#: tally_ho/apps/tally/templates/super_admin/form_audit.html:29 -#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:29 -#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:29 -#: tally_ho/apps/tally/templates/super_admin/form_progress.html:29 -msgid "Race Type" -msgstr "نوع التنافس" - -#: tally_ho/apps/tally/templates/data/candidates.html:41 -#: tally_ho/apps/tally/templates/data/tallies.html:34 -msgid "Last Modification" -msgstr "التعديل الاخير" - -#: tally_ho/apps/tally/templates/data/candidates.html:42 -#: tally_ho/apps/tally/templates/data/forms.html:84 -#: tally_ho/apps/tally/templates/data/races.html:34 -#: tally_ho/apps/tally/templates/data/tallies.html:36 -#: tally_ho/apps/tally/templates/data/users.html:64 -#: tally_ho/apps/tally/templates/reports/admin_areas_excluded_after_investigation.html:43 -#: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:43 -#: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:46 -#: tally_ho/apps/tally/templates/reports/progressive_report.html:46 -#: tally_ho/apps/tally/templates/reports/summary_report.html:47 -#: tally_ho/apps/tally/templates/reports/turnout_report.html:49 -#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_list.html:25 -msgid "Actions" -msgstr "تصحيحات" - #: tally_ho/apps/tally/templates/data/centers.html:18 #: tally_ho/apps/tally/templates/reports/candidate_list_by_votes.html:20 #: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:30 #: tally_ho/apps/tally/templates/reports/progressive_report.html:30 #: tally_ho/apps/tally/templates/reports/summary_report.html:30 -#: tally_ho/apps/tally/templates/reports/turnout_report.html:30 msgid "Region Name: " -msgstr "اسم المركز :" +msgstr "المنطقة :" #: tally_ho/apps/tally/templates/data/centers.html:21 #: tally_ho/apps/tally/templates/reports/candidate_list_by_votes.html:23 @@ -1169,77 +1171,90 @@ msgid "Center and Station List" msgstr "قائمة المركز / المحطة" #: tally_ho/apps/tally/templates/data/centers.html:35 -#: tally_ho/apps/tally/views/super_admin.py:1159 +#: tally_ho/apps/tally/views/super_admin.py:1513 msgid "New Station" msgstr "محطة" #: tally_ho/apps/tally/templates/data/centers.html:42 -#: tally_ho/apps/tally/views/super_admin.py:746 +#: tally_ho/apps/tally/views/super_admin.py:931 msgid "New Center" msgstr "تاريخ الانشاء" #: tally_ho/apps/tally/templates/data/centers.html:61 -#: tally_ho/apps/tally/templates/data/forms.html:77 -#: tally_ho/apps/tally/templates/reports/form_results.html:66 +#: tally_ho/apps/tally/templates/data/forms.html:75 #: tally_ho/apps/tally/templates/reports/offices.html:33 #: tally_ho/apps/tally/templates/reports/station_progress.html:30 -#: tally_ho/apps/tally/templates/super_admin/form_audit.html:27 -#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:27 +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:169 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:32 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:32 #: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:27 #: tally_ho/apps/tally/templates/super_admin/form_progress.html:27 msgid "Office" msgstr "مكتب" #: tally_ho/apps/tally/templates/data/centers.html:62 +#: tally_ho/apps/tally/templates/data/forms.html:78 #: tally_ho/apps/tally/templates/reports/station_progress.html:31 -msgid "Voting district" -msgstr "دائرة انتخابيه " +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:23 +msgid "Sub Con Name" +msgstr "اسم المركز" #: tally_ho/apps/tally/templates/data/centers.html:63 #: tally_ho/apps/tally/templates/data/forms.html:79 +#: tally_ho/apps/tally/templates/reports/station_progress.html:32 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:24 +msgid "Sub Con Code" +msgstr "الدائرة الفرعية :" + +#: tally_ho/apps/tally/templates/data/centers.html:64 +#: tally_ho/apps/tally/templates/data/forms.html:82 msgid "Region Name" -msgstr "اسم المركز" +msgstr "المنطقة" -#: tally_ho/apps/tally/templates/data/centers.html:67 -#: tally_ho/apps/tally/templates/reports/form_results.html:70 -#: tally_ho/apps/tally/templates/reports/station_progress.html:35 +#: tally_ho/apps/tally/templates/data/centers.html:68 +#: tally_ho/apps/tally/templates/reports/form_results.html:370 +#: tally_ho/apps/tally/templates/reports/station_progress.html:36 msgid "Gender" msgstr "الجنس" -#: tally_ho/apps/tally/templates/data/centers.html:68 -#: tally_ho/apps/tally/templates/reports/form_results.html:63 -#: tally_ho/apps/tally/templates/reports/station_progress.html:36 +#: tally_ho/apps/tally/templates/data/centers.html:69 +#: tally_ho/apps/tally/templates/reports/station_progress.html:37 msgid "Registrants" msgstr "عدد المسجلين" -#: tally_ho/apps/tally/templates/data/centers.html:69 +#: tally_ho/apps/tally/templates/data/centers.html:70 msgid "Received" msgstr "لم يتم الاستيلام" -#: tally_ho/apps/tally/templates/data/centers.html:70 -#: tally_ho/apps/tally/templates/data/forms.html:36 +#: tally_ho/apps/tally/templates/data/centers.html:71 +#: tally_ho/apps/tally/templates/data/forms.html:35 #: tally_ho/apps/tally/templates/reports/offices.html:31 -#: tally_ho/libs/reports/progress.py:174 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:34 +#: tally_ho/libs/reports/progress.py:175 msgid "Archived" msgstr "تم الارشفة" -#: tally_ho/apps/tally/templates/data/centers.html:71 +#: tally_ho/apps/tally/templates/data/centers.html:72 msgid "Center enabled" msgstr "اسم المركز" -#: tally_ho/apps/tally/templates/data/centers.html:72 -#: tally_ho/apps/tally/templates/reports/station_progress.html:37 +#: tally_ho/apps/tally/templates/data/centers.html:73 +#: tally_ho/apps/tally/templates/reports/station_progress.html:38 msgid "Station enabled" msgstr "رمز المحطة :" -#: tally_ho/apps/tally/templates/data/centers.html:74 -#: tally_ho/apps/tally/templates/data/races.html:52 +#: tally_ho/apps/tally/templates/data/centers.html:75 #: tally_ho/apps/tally/templates/super_admin/quarantine_checks_list.html:36 -#: tally_ho/libs/utils/templates.py:76 tally_ho/libs/utils/templates.py:92 -#: tally_ho/libs/utils/templates.py:108 +#: tally_ho/libs/utils/templates.py:92 tally_ho/libs/utils/templates.py:108 +#: tally_ho/libs/utils/templates.py:124 msgid "Edit" msgstr "يحرر" +#: tally_ho/apps/tally/templates/data/electrol_races.html:31 +#: tally_ho/apps/tally/views/super_admin.py:1277 +msgid "New Electrol Race" +msgstr "تحقق" + #: tally_ho/apps/tally/templates/data/forms.html:22 msgid "Export:" msgstr "تقارير " @@ -1250,120 +1265,124 @@ msgid "All" msgstr "كل" #: tally_ho/apps/tally/templates/data/forms.html:29 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:28 msgid "Unsubmitted" msgstr "غير مدخلة" #: tally_ho/apps/tally/templates/data/forms.html:30 #: tally_ho/apps/tally/templates/intake/clearance.html:7 -#: tally_ho/apps/tally/views/intake.py:80 -#: tally_ho/apps/tally/views/intake.py:165 -#: tally_ho/apps/tally/views/intake.py:224 -#: tally_ho/apps/tally/views/intake.py:251 -#: tally_ho/apps/tally/views/intake.py:288 -#: tally_ho/apps/tally/views/intake.py:434 -#: tally_ho/libs/reports/progress.py:184 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:29 +#: tally_ho/apps/tally/views/intake.py:79 +#: tally_ho/apps/tally/views/intake.py:164 +#: tally_ho/apps/tally/views/intake.py:211 +#: tally_ho/apps/tally/views/intake.py:238 +#: tally_ho/apps/tally/views/intake.py:275 +#: tally_ho/apps/tally/views/intake.py:421 +#: tally_ho/libs/reports/progress.py:185 msgid "Intake" msgstr "استقبال" #: tally_ho/apps/tally/templates/data/forms.html:31 -#: tally_ho/apps/tally/views/intake.py:435 -#: tally_ho/libs/reports/progress.py:204 +#: tally_ho/apps/tally/views/intake.py:422 +#: tally_ho/libs/reports/progress.py:205 msgid "Data Entry 1" msgstr "ادخال بيانات 1" #: tally_ho/apps/tally/templates/data/forms.html:32 -#: tally_ho/apps/tally/views/data_entry.py:402 -#: tally_ho/libs/reports/progress.py:214 +#: tally_ho/apps/tally/views/data_entry.py:409 +#: tally_ho/libs/reports/progress.py:215 msgid "Data Entry 2" msgstr "ادخال بيانات 2" #: tally_ho/apps/tally/templates/data/forms.html:33 -#: tally_ho/libs/reports/progress.py:224 +#: tally_ho/libs/reports/progress.py:225 msgid "Correction" msgstr "تصحيح" #: tally_ho/apps/tally/templates/data/forms.html:34 #: tally_ho/apps/tally/templates/quality_control/general.html:7 #: tally_ho/apps/tally/templates/quality_control/reject.html:7 -#: tally_ho/apps/tally/views/quality_control.py:291 -#: tally_ho/libs/reports/progress.py:234 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:33 +#: tally_ho/apps/tally/views/quality_control.py:277 +#: tally_ho/libs/reports/progress.py:235 msgid "Quality Control" msgstr "ضبط الجودة" -#: tally_ho/apps/tally/templates/data/forms.html:35 -#: tally_ho/apps/tally/views/quality_control.py:292 -msgid "Archiving" -msgstr "الارشفة" - -#: tally_ho/apps/tally/templates/data/forms.html:37 +#: tally_ho/apps/tally/templates/data/forms.html:36 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:35 #: tally_ho/apps/tally/views/clearance.py:337 -#: tally_ho/libs/reports/progress.py:194 +#: tally_ho/libs/reports/progress.py:195 msgid "Clearance" msgstr "تحقق" -#: tally_ho/apps/tally/templates/data/forms.html:38 -#: tally_ho/libs/reports/progress.py:244 +#: tally_ho/apps/tally/templates/data/forms.html:37 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:36 +#: tally_ho/libs/reports/progress.py:245 msgid "Audit" msgstr "التدقيق" -#: tally_ho/apps/tally/templates/data/forms.html:43 -msgid "Export Presidential Forms in JSON" -msgstr "تصدير النماذج الرئاسية بتنسيق JSON" - -#: tally_ho/apps/tally/templates/data/forms.html:44 -msgid "Export Parliamentary Forms in JSON" -msgstr "تصدير النماذج البرلمانية في JSON" +#: tally_ho/apps/tally/templates/data/forms.html:42 +#: tally_ho/apps/tally/templates/data/sub_cons_list.html:29 +msgid "json" +msgstr "json" -#: tally_ho/apps/tally/templates/data/forms.html:49 -#: tally_ho/apps/tally/views/super_admin.py:279 +#: tally_ho/apps/tally/templates/data/forms.html:47 +#: tally_ho/apps/tally/views/super_admin.py:302 msgid "New Form" msgstr "تسجيل أستمارة جديدة " -#: tally_ho/apps/tally/templates/data/forms.html:74 +#: tally_ho/apps/tally/templates/data/forms.html:72 msgid "Center Code" -msgstr "رقم المركز" +msgstr "رقم المركز الانتخابي" -#: tally_ho/apps/tally/templates/data/forms.html:75 +#: tally_ho/apps/tally/templates/data/forms.html:73 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:60 -#: tally_ho/apps/tally/templates/reports/form_results.html:68 msgid "Station ID" msgstr "محطة" -#: tally_ho/apps/tally/templates/data/forms.html:78 +#: tally_ho/apps/tally/templates/data/forms.html:76 #: tally_ho/apps/tally/templates/reports/offices.html:32 -#: tally_ho/apps/tally/templates/super_admin/form_audit.html:28 -#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:28 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:33 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:33 #: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:28 -#: tally_ho/apps/tally/templates/super_admin/form_progress.html:28 +#: tally_ho/apps/tally/templates/super_admin/form_progress.html:31 msgid "Office Number" msgstr "اسم المكتب :" -#: tally_ho/apps/tally/templates/data/forms.html:80 -#: tally_ho/apps/tally/templates/data/races.html:28 -#: tally_ho/apps/tally/templates/reports/form_results.html:73 -#: tally_ho/apps/tally/templates/reports/races.html:28 -msgid "Voting District" -msgstr "منطقة التصويت :" +#: tally_ho/apps/tally/templates/data/forms.html:77 +#: tally_ho/apps/tally/templates/data/sub_cons_list.html:39 +#: tally_ho/apps/tally/templates/reports/candidate_list_by_votes.html:43 +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:222 +#: tally_ho/apps/tally/templates/reports/form_results.html:375 +#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:23 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:31 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:31 +#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:26 +#: tally_ho/apps/tally/templates/super_admin/form_progress.html:26 +msgid "Ballot Number" +msgstr "الرقم التسلسلي" -#: tally_ho/apps/tally/templates/data/forms.html:82 -#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:25 -#: tally_ho/apps/tally/templates/super_admin/form_action.html:16 -#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:30 -#: tally_ho/apps/tally/templates/super_admin/form_progress.html:30 +#: tally_ho/apps/tally/templates/data/forms.html:83 +#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:26 +#: tally_ho/apps/tally/templates/super_admin/form_action.html:17 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:30 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:30 +#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:30 +#: tally_ho/apps/tally/templates/super_admin/form_progress.html:28 msgid "Form State" msgstr "حالة النمودج" -#: tally_ho/apps/tally/templates/data/forms.html:83 -#: tally_ho/apps/tally/templates/super_admin/form_action.html:18 -#: tally_ho/apps/tally/templates/super_admin/form_audit.html:31 -#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:31 +#: tally_ho/apps/tally/templates/data/forms.html:84 +#: tally_ho/apps/tally/templates/super_admin/form_action.html:19 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:35 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:35 #: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:32 -#: tally_ho/apps/tally/templates/super_admin/form_progress.html:32 +#: tally_ho/apps/tally/templates/super_admin/form_progress.html:33 msgid "Last Modified" msgstr "التعديل الاخير" #: tally_ho/apps/tally/templates/data/offices.html:18 -#: tally_ho/apps/tally/templates/super_admin/home.html:19 +#: tally_ho/apps/tally/templates/super_admin/home.html:21 msgid "Office List" msgstr "مكتب" @@ -1377,83 +1396,30 @@ msgstr "تقارير " msgid "ID" msgstr "بطاقة تعريف" -#: tally_ho/apps/tally/templates/data/offices.html:31 -#: tally_ho/apps/tally/templates/reports/offices.html:12 -msgid "Number" -msgstr "رقم " - #: tally_ho/apps/tally/templates/data/offices.html:32 -#: tally_ho/apps/tally/templates/reports/form_results.html:65 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2757 +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:175 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3383 msgid "Region" msgstr "منطقة" -#: tally_ho/apps/tally/templates/data/races.html:7 -#: tally_ho/apps/tally/templates/super_admin/home.html:21 -msgid "Races List" -msgstr "قائمة التصحيح" - -#: tally_ho/apps/tally/templates/data/races.html:17 -#: tally_ho/apps/tally/templates/reports/races.html:24 -msgid "Per Race" -msgstr "لكل مكتب" - -#: tally_ho/apps/tally/templates/data/races.html:21 -#: tally_ho/apps/tally/views/super_admin.py:1006 -msgid "New Race" -msgstr "لكل مكتب" - -#: tally_ho/apps/tally/templates/data/races.html:27 -#: tally_ho/apps/tally/templates/reports/form_results.html:82 -#: tally_ho/apps/tally/templates/reports/races.html:27 -msgid "Race Number" -msgstr "رقم التنافس" - -#: tally_ho/apps/tally/templates/data/races.html:30 -msgid "Race Attachments" -msgstr "مرفقات العرق" - -#: tally_ho/apps/tally/templates/data/races.html:31 -#: tally_ho/apps/tally/templates/reports/races.html:30 -msgid "Forms Expected" -msgstr "مترقب" - -#: tally_ho/apps/tally/templates/data/races.html:32 -#: tally_ho/apps/tally/templates/reports/races.html:31 -msgid "Forms Complete" -msgstr "استعراض مكتمل" - -#: tally_ho/apps/tally/templates/data/races.html:33 -msgid "Percent Complete" -msgstr "استعراض مكتمل" - -#: tally_ho/apps/tally/templates/data/races.html:54 -#: tally_ho/apps/tally/templates/super_admin/edit_center.html:48 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:49 -#: tally_ho/apps/tally/templates/super_admin/edit_station.html:56 -msgid "Disable" -msgstr "تاريخ الانشاء" - -#: tally_ho/apps/tally/templates/data/races.html:56 -#: tally_ho/apps/tally/templates/super_admin/edit_center.html:52 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:53 -#: tally_ho/apps/tally/templates/super_admin/edit_station.html:60 -msgid "Enable" -msgstr "تاريخ الانشاء" - -#: tally_ho/apps/tally/templates/data/races.html:70 -msgid "Page" -msgstr "صفحة" - -#: tally_ho/apps/tally/templates/data/races.html:70 -msgid "of" -msgstr "ل" - #: tally_ho/apps/tally/templates/data/regions.html:18 -#: tally_ho/apps/tally/templates/super_admin/home.html:18 +#: tally_ho/apps/tally/templates/super_admin/home.html:20 msgid "Region List" msgstr "قائمة المركز" +#: tally_ho/apps/tally/templates/data/sub_cons_list.html:35 +#: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:38 +#: tally_ho/apps/tally/templates/reports/progressive_report.html:38 +#: tally_ho/apps/tally/templates/reports/summary_report.html:38 +msgid "Code" +msgstr "شفرة" + +#: tally_ho/apps/tally/templates/data/sub_cons_list.html:38 +#: tally_ho/apps/tally/templates/reports/form_results.html:372 +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:26 +msgid "Sub Race" +msgstr "لكل مكتب" + #: tally_ho/apps/tally/templates/data/tallies.html:18 msgid "Tally List" msgstr "قائمة تالي" @@ -1472,7 +1438,7 @@ msgid "Administrators List" msgstr "ارسل الي المشرف" #: tally_ho/apps/tally/templates/data/users.html:21 -#: tally_ho/apps/tally/templates/super_admin/home.html:17 +#: tally_ho/apps/tally/templates/super_admin/home.html:19 #: tally_ho/apps/tally/templates/tally_manager/home.html:19 msgid "Users List" msgstr "قائمة المركز" @@ -1507,8 +1473,8 @@ msgid "Date Joined" msgstr "تاريخ تعديل الفريق :" #: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:7 -#: tally_ho/apps/tally/views/data_entry.py:73 -#: tally_ho/apps/tally/views/data_entry.py:407 +#: tally_ho/apps/tally/views/data_entry.py:80 +#: tally_ho/apps/tally/views/data_entry.py:414 msgid "Data Entry" msgstr "ادخال بيانات" @@ -1516,7 +1482,12 @@ msgstr "ادخال بيانات" msgid "Some fields are blank!" msgstr "بعض الحقول فارغة!" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:17 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:16 +msgid "Reconciliation section" +msgstr "قسم التسوية" + +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:21 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:10 msgid "" "In all the fields below, please check the number of ballots twice to ensure " "accuracy" @@ -1524,122 +1495,149 @@ msgstr "" "في جميع الخانات أدناه، يرجى التحقق من عدد أوراق الاقتراع مرتين لضمان " "الدقة" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:18 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:22 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:11 msgid "Section (A) to be filled prior to opening the polling station" msgstr "القسم (أ) يعبا قبل فتح المحطة" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:22 -msgid "Reconciliation section" -msgstr "قسم التسوية" - -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:29 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:12 -msgid "Serial number of ballots received by the polling station" -msgstr "الارقام التسلسية لاوراق الاقتراع التى تم استلامها من محطة الاقتراع" - -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:52 -msgid "section (d) to be filled after the completion of the results forms" -msgstr "القسم (ج) يعبأ بعد عد اوراق الاقتراع المستخرجة من صندوق الاقتراع" +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:32 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:21 +msgid "Total number of ballots papers received by the polling station" +msgstr "1 العدد الكلي لأوراق الاقتراع التي استلمتها المحطة" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:58 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:41 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:40 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:29 +msgid "" +"Section (B) reconciliation of the number of voter cards with ballot papers" +msgstr "القسم ( ب ) مطابقة عدد البطاقات بعدد أوراق الإقتراع" + +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:48 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:37 +msgid "Total of fields 5 + 7" +msgstr "مجموع الخانتين ( 5 + 7 )" + +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:61 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:50 +msgid "Number of voter cards in the ballot box:" +msgstr "عدد بطاقات الناخبين داخل الصندوق 2" + +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:71 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:60 +msgid "Section (E) to be filled in after completing the results forms" +msgstr "القسم ( هـ ) يعبأ بعد إكمال نماذج النتائج" + +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:77 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:66 msgid "Number of unstamped ballots:" -msgstr "عدد الاصوات الغير مختومة:" - -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:64 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:47 -msgid "Number of blank ballots:" -msgstr "عدد أوراق الاقتراع الغير مستخدمة:" +msgstr "عدد الاوراق غير مختومة" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:70 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:53 -msgid "Number of invalid votes (including the blanks):" -msgstr "عدد الأوراق الباطلة:" +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:83 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:72 +msgid "Number of invalid votes (including empty ballot papers):" +msgstr "عدد الأصوات الغير الصحيحة ( بما فيها الأوراق الخالية )" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:76 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:59 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:89 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:78 msgid "Number of valid votes:" msgstr "عدد الأصوات الصحيحة:" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:82 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:65 -msgid "Total number of the sorted and counted ballots:" -msgstr "مجموع أوراق الاقتراع المفروزة والمعدودة:" +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:95 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:84 +msgid "Total number of the sorted and counted ballots (10 + 11 + 12):" +msgstr "مجموع أوراق الاقتراع المفروزة والمعدودة ( 12 + 11 + 10 )" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:87 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:70 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:100 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:89 msgid "" -"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." +"NB: If the number in field 2 is not equal to the number in field 9, the " +"result should be recalculated in fields 5, 7." msgstr "" -"إذا كان الرقم في الخانة 12 غير مساوي للرقم في الخانة 7، يجب اعادة حساب " -"النتائج فى الخانات 9 و 10 و 11." +"ملاحظة: إذا كان الرقم في الخانة 8 لا يساوي الرقم في الخانة 1، ينبغي تكرار " +"الحسابات أعلاه." -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:90 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:73 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:103 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:92 msgid "" -"If after recounting, the difference persists, the ballots of the fields 9, " -"10 and 11 should be recounted as in the manual." +"If the difference remains after the recalculation, the cancelled ballot " +"papers and ballot papers in the box shall be recounted in fields 5 and 7." msgstr "" -"إذا،ظل الفرق قائما بعد إعادة الحساب ، يجب اعادة عد اوراق الاقتراع فى الخانات " -"9 و 10 و 11 كما مبين فى الدليل." +"إذا ظل الفرق بعد إعادة الحساب، يجب إعادة عد أوراق الاقتراع الملغاة وأوراق " +"الاقتراع الموجودة في الصندوق في الحقول 5 و 7." -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:93 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:76 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:106 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:115 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:95 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:104 msgid "" -"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." +"If the difference remains, write a note in the box below the form, stating " +"the procedures followed before proceeding to the next step." msgstr "" "إذا ظل الفرق، اكتب ملاحظة فى الخانة أسفل الاستمارة ذاكرا الإجراءات " "المتبعة قبل الانتقال للخطوة التالية." +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:109 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:98 +msgid "" +"If the number in field 13 is not equal to the number in field 7, the result " +"should be recalculated in fields 10, 11, and 12." +msgstr "" +"إذا كان الرقم في الخانة 12 غير مساوي للرقم في الخانة 7، يجب اعادة حساب " +"النتائج فى الخانات 9 و 10 و 11." + #: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:112 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:95 -msgid "Section (B) to be filled after closure of polling station" -msgstr "القسم (ب) يعبأ عند غلق المحطة" +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:101 +msgid "" +"If the difference remains after the recalculation, the ballot papers shall " +"be recounted in fields 10, 11 , and 12 as indicated in the procedures manual." +msgstr "" +"إذا ظل الفرق بعد إعادة الحساب، يجب إعادة عد أوراق الاقتراع في الحقول 10 و 11 " +"و 12 كما هو موضح في دليل الإجراءات." -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:120 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:103 -msgid "Number of signatures in the VR:" -msgstr "عدد التواقيع فى سجل الناخبين:" +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:134 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:123 +msgid "Section (C) to be filled in upon closing the station" +msgstr "القسم ( ج ) يعبأ عند غلق المحطة" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:126 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:109 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:141 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:130 msgid "Number of unused ballots:" msgstr "عدد أوراق الاقتراع الغير مستخدمة:" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:132 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:115 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:147 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:136 msgid "Number of spoiled ballots:" msgstr "عدد أوراق الاقتراع التالفة :" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:138 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:121 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:153 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:142 msgid "Number of cancelled ballots:" msgstr "عدد أوراق الاقتراع الملغاة :" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:144 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:127 -msgid "Total number of ballots remaining outside the ballot box:" -msgstr "العدد الكلي لأوراق الاقتراع خارج صندوق الاقتراع :" +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:159 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:148 +msgid "Total number of ballots remaining outside the ballot box (3 + 4 + 5):" +msgstr "6 العدد الكلي لأوراق الاقتراع خارج صندوق الاقتراع ( 5 + 4 + 3 )" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:150 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:133 -msgid "Section (C) to be filled after counting the ballots in the box" -msgstr "القسم (ج) يعبأ بعد عد أوراق الاقتراع فى صندوق الإقتراع" +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:165 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:154 +msgid "" +"Section (D) to be completed after counting the ballot papers in the ballot " +"box" +msgstr "القسم( د ) يعبأ بعد عد أوراق الاقتراع في صندوق الاقتراع" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:156 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:139 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:172 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:160 msgid "Number of ballots found inside the ballot box:" msgstr "عدد أوراق الاقتراع الموجودة داخل صندوق الاقتراع" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:162 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:145 -msgid "Total number of ballots found inside and outside the ballot box:" -msgstr "مجمود أوراق الاقتراع الموجودة داخل وخارج صندوق الاقتراع" +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:178 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:166 +msgid "" +"Total number of ballots found inside and outside the ballot box (6 + 7):" +msgstr "مجموع أوراق الاقتراع الموجودة داخل وخارج الصندوق ( 7 + 6 )" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:167 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:150 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:183 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:171 msgid "" "N.B. if the figure in field 8 does not equal the figure in field 1, the " "calculations above should be redone." @@ -1647,8 +1645,8 @@ msgstr "" "ملاحظة: إذا كان الرقم في الخانة 8 لا يساوي الرقم في الخانة 1، ينبغي تكرار " "الحسابات أعلاه." -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:170 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:153 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:186 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:174 msgid "" "If the difference persists, the ballots should be recounted to verify the " "figures in the fields 3, 4, 5, 7." @@ -1656,8 +1654,8 @@ msgstr "" "إذا ظل الفرق قائما ، يجي اعادة عدد أوراق الاقتراع للتحقق من الأرقام في " "الخانات 3، 4، 5، 7" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:173 -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:156 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:189 +#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:177 msgid "" "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." @@ -1665,18 +1663,23 @@ msgstr "" "إذا ظل الفرق، اكتب ملاحظة فى الخانة أسفل الاستمارة ذاكرا الإجراءات " "المتبعة قبل الانتقال للخطوة التالية." -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:184 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:197 +msgid "If needed, use this field for any necessary notes." +msgstr "إذا لزم الأمر، استخدم هذا الحقل لأي ملاحظات ضرورية." + +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:207 #: tally_ho/apps/tally/templates/quality_control/general.html:11 #: tally_ho/apps/tally/templates/quality_control/general.html:16 #: tally_ho/apps/tally/templates/quality_control/results.html:3 msgid "Results Section" msgstr "قسم النتائج" -#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:192 +#: tally_ho/apps/tally/templates/data_entry/enter_results_view.html:215 #: tally_ho/apps/tally/templates/quality_control/results.html:8 #: tally_ho/apps/tally/templates/reports/candidate_list_by_votes.html:41 #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:56 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:61 +#: tally_ho/apps/tally/templates/reports/form_results.html:367 #: tally_ho/apps/tally/templates/reports/votes_per_candidate.html:31 #: tally_ho/apps/tally/templates/super_admin/form_results_duplicates.html:20 msgid "Votes" @@ -1826,11 +1829,11 @@ msgstr "يرفض" msgid "Quality Control & Archiving" msgstr "ضبط الجودة" -#: tally_ho/apps/tally/templates/quality_control/dashboard.html:35 +#: tally_ho/apps/tally/templates/quality_control/dashboard.html:22 msgid "Correct" msgstr "صحيح" -#: tally_ho/apps/tally/templates/quality_control/dashboard.html:36 +#: tally_ho/apps/tally/templates/quality_control/dashboard.html:23 msgid "Incorrect" msgstr "غير صحيح" @@ -1866,10 +1869,6 @@ msgstr "المؤرشفة من قبل :" msgid "Reconciliation Section" msgstr "قسم التسوية" -#: tally_ho/apps/tally/templates/quality_control/reconciliation.html:35 -msgid "Section (d) to be filled after the completion of the results forms" -msgstr "القسم (ج) يعبأ بعد عد اوراق الاقتراع المستخرجة من صندوق الاقتراع" - #: tally_ho/apps/tally/templates/quality_control/reject.html:9 msgid "Form Rejected: Send to Data Entry 1" msgstr "الاستمارة مرفوضه : ارسال الى الادخال 1" @@ -1877,7 +1876,7 @@ msgstr "الاستمارة مرفوضه : ارسال الى الادخال 1" #: tally_ho/apps/tally/templates/reports/admin_areas_excluded_after_investigation.html:23 #: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:23 msgid "Region Name " -msgstr "اسم المركز" +msgstr "المنطقة" #: tally_ho/apps/tally/templates/reports/admin_areas_excluded_after_investigation.html:27 #: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:27 @@ -1892,6 +1891,7 @@ msgstr "مستبعد بعد التحقيق" #: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:40 #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:26 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:26 +#: tally_ho/apps/tally/templates/reports/form_results.html:302 #: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:45 msgid "Centers" msgstr "قائمة المركز" @@ -1901,7 +1901,7 @@ msgstr "قائمة المركز" #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:34 #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:58 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:34 -#: tally_ho/apps/tally/templates/reports/form_results.html:38 +#: tally_ho/apps/tally/templates/reports/form_results.html:310 #: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:44 msgid "Stations" msgstr "محطة" @@ -1920,7 +1920,7 @@ msgid "Sub Constituency Centers and Stations excluded after investigation" msgstr "استبعاد مراكز ومحطات الدوائر الفرعية بعد التحقيق" #: tally_ho/apps/tally/templates/reports/admin_areas_excluded_after_investigation.html:75 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2296 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2922 msgid "Region Constituencies excluded after investigation" msgstr "تم استبعاد الدوائر الانتخابية في المنطقة بعد التحقيق" @@ -1933,7 +1933,7 @@ msgid " under investigation" msgstr "قيد التحقيق" #: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:63 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2167 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2793 msgid "Constituency Centers and Stations under investigation" msgstr "مراكز ومحطات الدوائر الانتخابية قيد التحقيق" @@ -1942,12 +1942,12 @@ msgid "Sub Constituency Centers and Stations under investigation" msgstr "مراكز الدوائر الفرعية والمحطات قيد التحقيق" #: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:75 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2205 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2831 msgid "Region Constituencies under Investigation" msgstr "دوائر المنطقة قيد التحقيق" #: tally_ho/apps/tally/templates/reports/admin_areas_under_investigation.html:80 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2218 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2844 msgid "Region Centers and Stations under Investigation" msgstr "رقم المركز والمحطة غير متطابقيين" @@ -1960,33 +1960,33 @@ msgid "Turn Out Report" msgstr "تقرير الخروج" #: tally_ho/apps/tally/templates/reports/administrative_areas_reports.html:25 -#: tally_ho/apps/tally/templates/super_admin/home.html:34 +#: tally_ho/apps/tally/templates/super_admin/home.html:36 msgid "Votes Summary Report" msgstr "تقرير ملخص الأصوات" #: tally_ho/apps/tally/templates/reports/administrative_areas_reports.html:30 -#: tally_ho/apps/tally/templates/super_admin/home.html:37 +#: tally_ho/apps/tally/templates/super_admin/home.html:39 msgid "Process Discrepancy Reports" msgstr "تقارير التناقض في العملية" #: tally_ho/apps/tally/templates/reports/administrative_areas_reports.html:33 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2526 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3152 msgid "Stations and Centers under process audit" msgstr "المحطات والمراكز قيد المراجعة العملية" #: tally_ho/apps/tally/templates/reports/administrative_areas_reports.html:38 -#: tally_ho/apps/tally/templates/super_admin/home.html:40 +#: tally_ho/apps/tally/templates/super_admin/home.html:42 msgid "Stations and Centers under Investigation" msgstr "المحطات والمراكز قيد التحقيق" #: tally_ho/apps/tally/templates/reports/administrative_areas_reports.html:43 -#: tally_ho/apps/tally/templates/super_admin/home.html:41 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2534 +#: tally_ho/apps/tally/templates/super_admin/home.html:43 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3160 msgid "Stations and Centers excluded after investigation" msgstr "استبعاد المحطات والمراكز بعد التحقيق" #: tally_ho/apps/tally/templates/reports/administrative_areas_reports.html:48 -#: tally_ho/apps/tally/templates/super_admin/home.html:35 +#: tally_ho/apps/tally/templates/super_admin/home.html:37 msgid "Progressive Report" msgstr "تقرير مركز الاحصاء" @@ -1996,20 +1996,10 @@ msgstr "مرشح" #: tally_ho/apps/tally/templates/reports/candidate_list_by_votes.html:40 #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:55 -#: tally_ho/apps/tally/templates/reports/form_results.html:59 #: tally_ho/apps/tally/templates/reports/votes_per_candidate.html:30 msgid "Candidate Name" msgstr "مرشح" -#: tally_ho/apps/tally/templates/reports/candidate_list_by_votes.html:43 -#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:23 -#: tally_ho/apps/tally/templates/super_admin/form_audit.html:26 -#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:26 -#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:26 -#: tally_ho/apps/tally/templates/super_admin/form_progress.html:26 -msgid "Ballot Number" -msgstr "الرقم التسلسلي" - #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:44 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:44 msgid "Filter out Centers and Stations from report." @@ -2017,13 +2007,15 @@ msgstr "Enter the centre number to delete." #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:45 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:45 -#: tally_ho/apps/tally/templates/reports/form_results.html:49 +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:211 +#: tally_ho/apps/tally/templates/reports/form_results.html:355 msgid "Apply" msgstr "يتقدم" #: tally_ho/apps/tally/templates/reports/candidates_votes_report.html:46 #: tally_ho/apps/tally/templates/reports/duplicate_results.html:46 -#: tally_ho/apps/tally/templates/reports/form_results.html:50 +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:212 +#: tally_ho/apps/tally/templates/reports/form_results.html:356 msgid "Reset" msgstr "إعادة ضبط" @@ -2070,7 +2062,6 @@ msgid "Result Forms with Duplicate Results" msgstr "متابعة النماذج" #: tally_ho/apps/tally/templates/reports/duplicate_results.html:56 -#: tally_ho/apps/tally/templates/reports/form_results.html:67 #: tally_ho/apps/tally/views/reports/overall_votes.py:26 #: tally_ho/apps/tally/views/reports/votes_per_candidate.py:91 #: tally_ho/libs/utils/templates.py:17 @@ -2082,64 +2073,115 @@ msgstr "تعريف المركز" msgid "State" msgstr "حالة النمودج" -#: tally_ho/apps/tally/templates/reports/form_results.html:19 -msgid "Form Results" -msgstr "لا توجد نتائج" +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:120 +#: tally_ho/apps/tally/templates/super_admin/home.html:55 +msgid "Election Statistics" +msgstr "نتائج استمارات التصحيح" -#: tally_ho/apps/tally/templates/reports/form_results.html:26 +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:202 +msgid "Station Gender" +msgstr "رمز المحطة :" + +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:210 +#: tally_ho/apps/tally/templates/reports/form_results.html:354 +msgid "Filters." +msgstr "الفلاتر." + +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:223 +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:177 +msgid "Stations Expected" +msgstr "رمز المحطة :" + +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:224 +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:178 +msgid "Stations Counted" +msgstr "رمز المحطة :" + +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:225 +#, python-format +msgid "(%%) of Stations Counted" +msgstr "رمز المحطة :" + +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:226 +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:180 +msgid "Registrants in Counted Stations" +msgstr "المسجلون في المحطات الم counted" + +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:227 +msgid "Voters in Counted Stations" +msgstr "قائمة المركز / المحطة" + +#: tally_ho/apps/tally/templates/reports/election_statistics_report.html:228 +#, python-format +msgid "(%%) Turnout in Counted Stations" +msgstr "" + +#: tally_ho/apps/tally/templates/reports/form_results.html:248 +#: tally_ho/apps/tally/templates/super_admin/result_export.html:9 +msgid "Candidate Results" +msgstr "مرشح" + +#: tally_ho/apps/tally/templates/reports/form_results.html:256 msgid "Export Presidential Results in JSON" msgstr "تصدير النتائج الرئاسية بتنسيق JSON" -#: tally_ho/apps/tally/templates/reports/form_results.html:27 +#: tally_ho/apps/tally/templates/reports/form_results.html:259 msgid "Export Parliamentary Results in JSON" msgstr "تصدير النتائج البرلمانية في JSON" -#: tally_ho/apps/tally/templates/reports/form_results.html:48 -msgid "Filter out Centers and Stations from results." -msgstr "قائمة المركز / المحطة" +#: tally_ho/apps/tally/templates/reports/form_results.html:262 +msgid "Number of leading candidates:" +msgstr "عدد الأوراق الصحيحة" -#: tally_ho/apps/tally/templates/reports/form_results.html:60 -#: tally_ho/apps/tally/templates/reports/overall_votes.html:14 -msgid "Total Votes" -msgstr "الاصوات" +#: tally_ho/apps/tally/templates/reports/form_results.html:268 +msgid "All Results JSON Export" +msgstr "تصدير جميع النتائج بصيغة JSON" -#: tally_ho/apps/tally/templates/reports/form_results.html:61 -msgid "Invalid Ballots" -msgstr "أوراق الاقتراع الباطلة" +#: tally_ho/apps/tally/templates/reports/form_results.html:269 +msgid "PowerPoint Export" +msgstr "تصدير PowerPoint" -#: tally_ho/apps/tally/templates/reports/form_results.html:62 -#: tally_ho/apps/tally/templates/reports/offices.html:34 -#: tally_ho/libs/reports/progress.py:264 -msgid "Valid Votes" -msgstr "الاصوات" +#: tally_ho/apps/tally/templates/reports/form_results.html:276 +msgid "Election Levels" +msgstr "مستويات الانتخابات" -#: tally_ho/apps/tally/templates/reports/form_results.html:64 -msgid "Candidate Status" -msgstr "مرشح" +#: tally_ho/apps/tally/templates/reports/form_results.html:284 +msgid "Sub Races" +msgstr "سباقات" -#: tally_ho/apps/tally/templates/reports/form_results.html:75 -msgid "Unstamped Ballots" -msgstr "عدد الأوراق الغير مختومة" +#: tally_ho/apps/tally/templates/reports/form_results.html:292 +#: tally_ho/apps/tally/templates/reports/progressive_report.html:45 +#: tally_ho/apps/tally/templates/reports/summary_report.html:46 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2317 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3514 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3688 +msgid "Sub Constituencies" +msgstr "الدائرة الفرعية :" -#: tally_ho/apps/tally/templates/reports/form_results.html:76 -msgid "Cancelled Ballots" -msgstr "عدد أوراق الاقتراع الملغاة" +#: tally_ho/apps/tally/templates/reports/form_results.html:318 +msgid "Ballot Status" +msgstr "محطة" -#: tally_ho/apps/tally/templates/reports/form_results.html:77 -msgid "Spoilt Ballots" -msgstr "الرقم التسلسلي" +#: tally_ho/apps/tally/templates/reports/form_results.html:328 +msgid "Station Status" +msgstr "محطة" -#: tally_ho/apps/tally/templates/reports/form_results.html:78 -msgid "Unused Ballots" -msgstr "عدد أوراق الاقتراع الغير مستخدمة" +#: tally_ho/apps/tally/templates/reports/form_results.html:336 +msgid "Candidate Status" +msgstr "مرشح" + +#: tally_ho/apps/tally/templates/reports/form_results.html:344 +msgid "Station Processed Percentage" +msgstr "تقرير مركز الاحصاء" -#: tally_ho/apps/tally/templates/reports/form_results.html:79 -msgid "Number of Signatures" -msgstr "عدد التواقيع فى سجل الناخبين" +#: tally_ho/apps/tally/templates/reports/form_results.html:368 +#: tally_ho/apps/tally/templates/reports/overall_votes.html:14 +msgid "Total Votes" +msgstr "الاصوات" -#: tally_ho/apps/tally/templates/reports/form_results.html:80 -msgid "Received Ballots Papers" -msgstr "أوراق الاقتراع المستلمة" +#: tally_ho/apps/tally/templates/reports/form_results.html:373 +msgid "Sub Con" +msgstr "الدوائر الفرعية" #: tally_ho/apps/tally/templates/reports/offices.html:7 msgid "Tally Center Progress Report" @@ -2174,10 +2216,15 @@ msgid "Not Intaken" msgstr "لم تستقبل" #: tally_ho/apps/tally/templates/reports/offices.html:30 -#: tally_ho/libs/reports/progress.py:162 +#: tally_ho/libs/reports/progress.py:163 msgid "Intaken" msgstr "استقبال" +#: tally_ho/apps/tally/templates/reports/offices.html:34 +#: tally_ho/libs/reports/progress.py:265 +msgid "Valid Votes" +msgstr "الاصوات" + #: tally_ho/apps/tally/templates/reports/offices.html:35 msgid "Office Candidates" msgstr "مرشح" @@ -2192,7 +2239,7 @@ msgstr "مرشح" #: tally_ho/apps/tally/templates/reports/overall_votes.html:37 #: tally_ho/apps/tally/templates/reports/overall_votes.html:41 -#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:42 +#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:44 msgid "View" msgstr "منظر" @@ -2208,13 +2255,6 @@ msgstr "دوائر المنطقة" msgid " Constituency " msgstr "الدائرة الفرعية :" -#: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:38 -#: tally_ho/apps/tally/templates/reports/progressive_report.html:38 -#: tally_ho/apps/tally/templates/reports/summary_report.html:38 -#: tally_ho/apps/tally/templates/reports/turnout_report.html:38 -msgid "Code" -msgstr "شفرة" - #: tally_ho/apps/tally/templates/reports/process_discrepancy_report.html:42 msgid "Total Centers" msgstr "تاريخ الانشاء" @@ -2223,6 +2263,10 @@ msgstr "تاريخ الانشاء" msgid "Total Stations" msgstr "محطة" +#: tally_ho/apps/tally/templates/reports/progress_report_by_admin_area.html:313 +msgid "Progress Report" +msgstr "تقرير مركز الاحصاء" + #: tally_ho/apps/tally/templates/reports/progressive_report.html:19 msgid " Regions Progressive Report" msgstr "تقرير مركز الاحصاء" @@ -2245,21 +2289,10 @@ msgstr "عدد الأوراق الصحيحة" #: tally_ho/apps/tally/templates/reports/progressive_report.html:44 #: tally_ho/apps/tally/templates/reports/summary_report.html:45 -#: tally_ho/apps/tally/templates/reports/turnout_report.html:47 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2888 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3513 msgid "Constituencies" msgstr "الدائرة الفرعية :" -#: tally_ho/apps/tally/templates/reports/progressive_report.html:45 -#: tally_ho/apps/tally/templates/reports/summary_report.html:46 -#: tally_ho/apps/tally/templates/reports/turnout_report.html:48 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1500 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1693 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2889 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3064 -msgid "Sub Constituencies" -msgstr "الدائرة الفرعية :" - #: tally_ho/apps/tally/templates/reports/races.html:7 msgid "Races Progress Report" msgstr "تقرير مركز الاحصاء" @@ -2272,6 +2305,31 @@ msgstr "سباقات" msgid "Completed" msgstr "استعراض مكتمل" +#: tally_ho/apps/tally/templates/reports/races.html:24 +msgid "Per Race" +msgstr "لكل مكتب" + +#: tally_ho/apps/tally/templates/reports/races.html:27 +msgid "Race Number" +msgstr "رقم التنافس" + +#: tally_ho/apps/tally/templates/reports/races.html:28 +msgid "Voting District" +msgstr "الدائرة الانتخابية :" + +#: tally_ho/apps/tally/templates/reports/races.html:29 +#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:29 +msgid "Race Type" +msgstr "نوع التنافس" + +#: tally_ho/apps/tally/templates/reports/races.html:30 +msgid "Forms Expected" +msgstr "مترقب" + +#: tally_ho/apps/tally/templates/reports/races.html:31 +msgid "Forms Complete" +msgstr "استعراض مكتمل" + #: tally_ho/apps/tally/templates/reports/races.html:32 msgid "Percentage Complete" msgstr "استعراض مكتمل" @@ -2350,37 +2408,32 @@ msgstr "معدل موافقة المشرفين المتميزين" msgid "Tally Managers Approval Rate" msgstr "معدل موافقة المديرين" -#: tally_ho/apps/tally/templates/reports/turnout_report.html:19 -msgid " Regions Turn Out Report" -msgstr "تقرير إطفاء المناطق" - -#: tally_ho/apps/tally/templates/reports/turnout_report.html:24 -msgid " Region Turn Out Report" -msgstr "تقرير اغلاق المنطقة" - -#: tally_ho/apps/tally/templates/reports/turnout_report.html:29 -msgid " Constituency Turn Out Report" -msgstr "الدائرة الانتخابية تقرير اغلاق" +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:145 +#: tally_ho/apps/tally/templates/super_admin/home.html:35 +msgid "Turnout Report" +msgstr "تقرير الاقبال" -#: tally_ho/apps/tally/templates/reports/turnout_report.html:42 -msgid "Total voters" -msgstr "مجموع المصوتين" +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:171 +msgid "Main-Constituency" +msgstr "الدائرة الفرعية :" -#: tally_ho/apps/tally/templates/reports/turnout_report.html:43 -msgid "Voters voted" -msgstr "صوت الناخبون" +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:173 +msgid "Sub constituency" +msgstr "الدائرة الفرعية :" -#: tally_ho/apps/tally/templates/reports/turnout_report.html:44 -msgid "Male voters" -msgstr "الناخبون الذكور" +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:179 +#, python-format +msgid "%% Progress" +msgstr "متابعة النماذج" -#: tally_ho/apps/tally/templates/reports/turnout_report.html:45 -msgid "Female voters" -msgstr "انثي" +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:181 +msgid "Votes Cast in Counted Stations" +msgstr "الأصوات المدلى بها في المحطات التي تم عدها" -#: tally_ho/apps/tally/templates/reports/turnout_report.html:46 -msgid "Turnout percentage" -msgstr "النسبة" +#: tally_ho/apps/tally/templates/reports/turnout_report_by_admin_area.html:182 +#, python-format +msgid "%% Turnout" +msgstr "" #: tally_ho/apps/tally/templates/reports/votes_per_candidate.html:17 msgid " Votes per Candidate" @@ -2392,16 +2445,14 @@ msgstr "نقل استمارة ناجحة الى " #: tally_ho/apps/tally/templates/super_admin/disable_entity.html:7 #: tally_ho/apps/tally/templates/super_admin/disable_entity.html:40 -#, fuzzy, python-format -#| msgid "Disable %(entityName)s" +#, python-format msgid "Disable %(entityName)s" -msgstr "تاريخ الانشاء" +msgstr "تعطيل %(entityName)s" #: tally_ho/apps/tally/templates/super_admin/disable_entity.html:9 -#, fuzzy, python-format -#| msgid "Select %(entity)s classification." +#, python-format msgid "Select %(entity)s classification." -msgstr "اختر تصنيف مركز" +msgstr "اختر تصنيف %(entity)s." #: tally_ho/apps/tally/templates/super_admin/duplicate_result_form.html:7 msgid "Duplicate result forms list for ballot: " @@ -2428,27 +2479,21 @@ msgid "Close" msgstr "يغلق" #: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:7 -#: tally_ho/apps/tally/templates/super_admin/home.html:26 +#: tally_ho/apps/tally/templates/super_admin/home.html:28 msgid "Duplicate Result Tracking" msgstr "تتبع نتيجة مكرر" -#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:49 +#: tally_ho/apps/tally/templates/super_admin/duplicate_result_tracking.html:51 msgid "No matching records found" msgstr "لم يتم العثور على سجلات مطابقة" -#: tally_ho/apps/tally/templates/super_admin/edit_center.html:7 -msgid "Edit Center" -msgstr "تاريخ الانشاء" - -#: tally_ho/apps/tally/templates/super_admin/edit_center.html:18 -#: tally_ho/apps/tally/templates/super_admin/edit_station.html:14 -#: tally_ho/apps/tally/templates/super_admin/remove_center_confirmation.html:11 -#: tally_ho/apps/tally/templates/super_admin/remove_station_confirmation.html:11 -msgid "Center:" -msgstr "تعريف المركز" +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:7 +msgid "Edit Ballot" +msgstr "منظومة النتائج للناخبيين الليبين" +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:23 #: tally_ho/apps/tally/templates/super_admin/edit_center.html:23 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:23 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:23 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:31 #: tally_ho/apps/tally/templates/super_admin/remove_center_confirmation.html:28 #: tally_ho/apps/tally/templates/super_admin/remove_station_confirmation.html:24 @@ -2456,35 +2501,63 @@ msgstr "تعريف المركز" msgid "Status:" msgstr "حالة النمودج" +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:25 #: tally_ho/apps/tally/templates/super_admin/edit_center.html:25 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:25 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:25 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:33 msgid "Enabled" msgstr "مرشح" +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:27 #: tally_ho/apps/tally/templates/super_admin/edit_center.html:27 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:27 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:27 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:35 msgid "Disabled" msgstr "مرشح" +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:31 #: tally_ho/apps/tally/templates/super_admin/edit_center.html:31 -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:31 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:31 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:39 msgid "Comments:" msgstr "تعليق الفريق:" +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:49 +#: tally_ho/apps/tally/templates/super_admin/edit_center.html:48 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:49 +#: tally_ho/apps/tally/templates/super_admin/edit_station.html:56 +msgid "Disable" +msgstr "تاريخ الانشاء" + +#: tally_ho/apps/tally/templates/super_admin/edit_ballot.html:53 +#: tally_ho/apps/tally/templates/super_admin/edit_center.html:52 +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:53 +#: tally_ho/apps/tally/templates/super_admin/edit_station.html:60 +msgid "Enable" +msgstr "تاريخ الانشاء" + +#: tally_ho/apps/tally/templates/super_admin/edit_center.html:7 +msgid "Edit Center" +msgstr "تاريخ الانشاء" + +#: tally_ho/apps/tally/templates/super_admin/edit_center.html:18 +#: tally_ho/apps/tally/templates/super_admin/edit_station.html:14 +#: tally_ho/apps/tally/templates/super_admin/remove_center_confirmation.html:11 +#: tally_ho/apps/tally/templates/super_admin/remove_station_confirmation.html:11 +msgid "Center:" +msgstr "تعريف المركز" + #: tally_ho/apps/tally/templates/super_admin/edit_center.html:56 #: tally_ho/apps/tally/templates/super_admin/edit_station.html:70 #: tally_ho/apps/tally/templates/tally_manager/edit_user_profile.html:34 #: tally_ho/apps/tally/templates/tally_manager/edit_user_profile.html:40 #: tally_ho/apps/tally/templates/tally_manager/tally_form.html:54 -#: tally_ho/libs/utils/templates.py:109 +#: tally_ho/libs/utils/templates.py:125 msgid "Delete" msgstr "تاريخ الانشاء" -#: tally_ho/apps/tally/templates/super_admin/edit_race.html:7 -msgid "Edit Race" +#: tally_ho/apps/tally/templates/super_admin/edit_electrol_race.html:7 +msgid "Edit Electrol Race" msgstr "تحرير العرق" #: tally_ho/apps/tally/templates/super_admin/edit_result_form.html:7 @@ -2526,11 +2599,11 @@ msgstr "تمكين الإجراء غير مسموح به لأن المركز م msgid "Forms Waiting For Approval" msgstr "نماذج قيد الانتظار" -#: tally_ho/apps/tally/templates/super_admin/form_action.html:17 +#: tally_ho/apps/tally/templates/super_admin/form_action.html:18 msgid "Action Required Prior" msgstr "العمل المطلوب قبل" -#: tally_ho/apps/tally/templates/super_admin/form_action.html:43 +#: tally_ho/apps/tally/templates/super_admin/form_action.html:45 msgid "Confirm" msgstr "موافقه" @@ -2538,22 +2611,30 @@ msgstr "موافقه" msgid "Form Audit List" msgstr "قائمة المراجعة" -#: tally_ho/apps/tally/templates/super_admin/form_audit.html:30 -#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:30 -#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:31 -#: tally_ho/apps/tally/templates/super_admin/form_progress.html:31 -msgid "Number of times rejected to DE1" -msgstr "عدد المرات المرفوضه من ادخال البيانات 1" +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:26 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:26 +msgid "Action Prior" +msgstr "العمل المطلوب قبل" -#: tally_ho/apps/tally/templates/super_admin/form_audit.html:32 -#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:32 +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:27 +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:27 msgid "Resolution Recomendation" msgstr "الحلول الموصى بها" +#: tally_ho/apps/tally/templates/super_admin/form_audit.html:34 +#: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:31 +#: tally_ho/apps/tally/templates/super_admin/form_progress.html:32 +msgid "Number of times rejected to DE1" +msgstr "عدد المرات المرفوضه من ادخال البيانات 1" + #: tally_ho/apps/tally/templates/super_admin/form_clearance.html:18 msgid "Form Clearance List" msgstr "قائمة التصحيح" +#: tally_ho/apps/tally/templates/super_admin/form_clearance.html:34 +msgid "Number of times rejected to PreIntake" +msgstr "عدد المرات المرفوضه من ادخال البيانات 1" + #: tally_ho/apps/tally/templates/super_admin/form_duplicates.html:18 msgid "Form Duplicates List" msgstr "متابعة النماذج" @@ -2562,6 +2643,22 @@ msgstr "متابعة النماذج" msgid "Form Progress List" msgstr "متابعة النماذج" +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:18 +msgid "Form Progress by Form States List" +msgstr "متابعة النماذج" + +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:27 +msgid "Total forms" +msgstr "إجمالي الأخطاء" + +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:30 +msgid "DE1" +msgstr "1 إدخال البيانات" + +#: tally_ho/apps/tally/templates/super_admin/form_progress_by_form_state.html:31 +msgid "DE2" +msgstr "2 إدخال البيانات" + #: tally_ho/apps/tally/templates/super_admin/form_results_duplicates.html:7 msgid "Results Duplicated List" msgstr "متابعة النماذج" @@ -2571,175 +2668,196 @@ msgstr "متابعة النماذج" msgid "Data views" msgstr "عرض البيانات" -#: tally_ho/apps/tally/templates/super_admin/home.html:20 +#: tally_ho/apps/tally/templates/super_admin/home.html:17 +#: tally_ho/apps/tally/views/data/electrol_race_list_view.py:63 +msgid "Electrol Races List" +msgstr "قائمة التصحيح" + +#: tally_ho/apps/tally/templates/super_admin/home.html:18 +#: tally_ho/apps/tally/views/data/sub_constituency_list_view.py:88 +msgid "Sub Constituencies List" +msgstr "الدائرة الفرعية :" + +#: tally_ho/apps/tally/templates/super_admin/home.html:22 msgid "Center List" msgstr "قائمة المركز" -#: tally_ho/apps/tally/templates/super_admin/home.html:22 -#: tally_ho/apps/tally/views/data/form_list_view.py:132 +#: tally_ho/apps/tally/templates/super_admin/home.html:23 +#: tally_ho/apps/tally/views/data/ballot_list_view.py:65 +msgid "Ballot List" +msgstr "قائمة تالي" + +#: tally_ho/apps/tally/templates/super_admin/home.html:24 +#: tally_ho/apps/tally/views/data/form_list_view.py:225 msgid "Form List" msgstr "قائمة النمودج " -#: tally_ho/apps/tally/templates/super_admin/home.html:23 -#: tally_ho/apps/tally/views/data/candidate_list_view.py:77 +#: tally_ho/apps/tally/templates/super_admin/home.html:25 +#: tally_ho/apps/tally/views/data/candidate_list_view.py:78 msgid "Candidate List" msgstr "مرشح" -#: tally_ho/apps/tally/templates/super_admin/home.html:24 -#: tally_ho/apps/tally/views/data/form_list_view.py:157 +#: tally_ho/apps/tally/templates/super_admin/home.html:26 +#: tally_ho/apps/tally/views/data/form_list_view.py:248 msgid "Forms Not Received" msgstr "لم يتم الاستيلام" -#: tally_ho/apps/tally/templates/super_admin/home.html:25 +#: tally_ho/apps/tally/templates/super_admin/home.html:27 msgid "Forms Waiting for Approval" msgstr "نماذج قيد الانتظار" -#: tally_ho/apps/tally/templates/super_admin/home.html:27 +#: tally_ho/apps/tally/templates/super_admin/home.html:29 msgid "Quarantine Checks" msgstr "الموقوف" -#: tally_ho/apps/tally/templates/super_admin/home.html:31 +#: tally_ho/apps/tally/templates/super_admin/home.html:33 msgid "Reports" msgstr "تقارير " -#: tally_ho/apps/tally/templates/super_admin/home.html:33 -msgid "Turnout Report" -msgstr "تقرير الاقبال" - -#: tally_ho/apps/tally/templates/super_admin/home.html:39 +#: tally_ho/apps/tally/templates/super_admin/home.html:41 msgid "Stations and Centers under Process Audit" msgstr "المحطات والمراكز قيد التدقيق العملية" -#: tally_ho/apps/tally/templates/super_admin/home.html:44 -msgid "Reports Races" -msgstr "تقارير" - -#: tally_ho/apps/tally/templates/super_admin/home.html:45 +#: tally_ho/apps/tally/templates/super_admin/home.html:46 msgid "Form Progress" msgstr "متابعة النماذج" -#: tally_ho/apps/tally/templates/super_admin/home.html:46 +#: tally_ho/apps/tally/templates/super_admin/home.html:47 +msgid "Progress by Sub Races Report" +msgstr "تقرير مركز الاحصاء" + +#: tally_ho/apps/tally/templates/super_admin/home.html:48 +msgid "Form Progress by Form State" +msgstr "متابعة النماذج" + +#: tally_ho/apps/tally/templates/super_admin/home.html:49 +#: tally_ho/apps/tally/templates/super_admin/home.html:59 msgid "Station Overall Votes" msgstr "الرقم التسلسلي غير مطابق" -#: tally_ho/apps/tally/templates/super_admin/home.html:47 +#: tally_ho/apps/tally/templates/super_admin/home.html:50 +#: tally_ho/apps/tally/templates/super_admin/home.html:60 msgid "Center Overall Votes" msgstr "قائمة المركز / المحطة" -#: tally_ho/apps/tally/templates/super_admin/home.html:48 +#: tally_ho/apps/tally/templates/super_admin/home.html:52 +msgid "Election Statistics Reports" +msgstr "نتائج استمارات التصحيح" + +#: tally_ho/apps/tally/templates/super_admin/home.html:61 msgid "Station Progress" msgstr "متابعة النماذج" -#: tally_ho/apps/tally/templates/super_admin/home.html:49 +#: tally_ho/apps/tally/templates/super_admin/home.html:62 msgid "Form Duplicates" msgstr "حالة النمودج" -#: tally_ho/apps/tally/templates/super_admin/home.html:50 +#: tally_ho/apps/tally/templates/super_admin/home.html:63 msgid "Form Clearance" msgstr "تحقق" -#: tally_ho/apps/tally/templates/super_admin/home.html:51 +#: tally_ho/apps/tally/templates/super_admin/home.html:64 msgid "Form Audit" msgstr "قائمة النمودج " -#: tally_ho/apps/tally/templates/super_admin/home.html:52 +#: tally_ho/apps/tally/templates/super_admin/home.html:65 msgid "Form Results Duplicates" msgstr "حالة النمودج" -#: tally_ho/apps/tally/templates/super_admin/home.html:53 +#: tally_ho/apps/tally/templates/super_admin/home.html:66 msgid "Download Results" msgstr "استمارة نتائج فارغة" -#: tally_ho/apps/tally/templates/super_admin/home.html:56 +#: tally_ho/apps/tally/templates/super_admin/home.html:69 msgid "Other user views" msgstr "مستخدم اخرى" -#: tally_ho/apps/tally/templates/super_admin/home.html:58 -#: tally_ho/apps/tally/templates/super_admin/home.html:75 +#: tally_ho/apps/tally/templates/super_admin/home.html:71 +#: tally_ho/apps/tally/templates/super_admin/home.html:88 msgid "Intake Clerk" msgstr "موظف الاستقبال " -#: tally_ho/apps/tally/templates/super_admin/home.html:59 -#: tally_ho/apps/tally/templates/super_admin/home.html:77 +#: tally_ho/apps/tally/templates/super_admin/home.html:72 +#: tally_ho/apps/tally/templates/super_admin/home.html:90 msgid "Clearance Clerk" msgstr "من قام بالتصحيح" -#: tally_ho/apps/tally/templates/super_admin/home.html:60 +#: tally_ho/apps/tally/templates/super_admin/home.html:73 msgid "Data Entry Clerk" msgstr "موظف الادخال" -#: tally_ho/apps/tally/templates/super_admin/home.html:61 -#: tally_ho/apps/tally/templates/super_admin/home.html:81 +#: tally_ho/apps/tally/templates/super_admin/home.html:74 +#: tally_ho/apps/tally/templates/super_admin/home.html:94 msgid "Corrections Clerk" msgstr "موظف التصحيح" -#: tally_ho/apps/tally/templates/super_admin/home.html:62 +#: tally_ho/apps/tally/templates/super_admin/home.html:75 msgid "Quality Control & Archiving Clerk" msgstr "موظف ضمان الجودة" -#: tally_ho/apps/tally/templates/super_admin/home.html:63 -#: tally_ho/apps/tally/templates/super_admin/home.html:84 +#: tally_ho/apps/tally/templates/super_admin/home.html:76 +#: tally_ho/apps/tally/templates/super_admin/home.html:97 msgid "Audit Clerk" msgstr "موظف التدقيق" -#: tally_ho/apps/tally/templates/super_admin/home.html:67 +#: tally_ho/apps/tally/templates/super_admin/home.html:80 msgid "Admin Operations" msgstr "عمليات الادارة" -#: tally_ho/apps/tally/templates/super_admin/home.html:69 +#: tally_ho/apps/tally/templates/super_admin/home.html:82 msgid "Remove a Center" msgstr "إزالة مركز" -#: tally_ho/apps/tally/templates/super_admin/home.html:70 +#: tally_ho/apps/tally/templates/super_admin/home.html:83 msgid "Remove a Station" msgstr "محطة" -#: tally_ho/apps/tally/templates/super_admin/home.html:73 +#: tally_ho/apps/tally/templates/super_admin/home.html:86 msgid "Staff Performance Metrics" msgstr "مقاييس أداء الموظفين" -#: tally_ho/apps/tally/templates/super_admin/home.html:76 +#: tally_ho/apps/tally/templates/super_admin/home.html:89 msgid "Intake Supervisor" msgstr "مشرف القبول" -#: tally_ho/apps/tally/templates/super_admin/home.html:78 +#: tally_ho/apps/tally/templates/super_admin/home.html:91 msgid "Clearance Supervisor" msgstr "مشرف التخليص" -#: tally_ho/apps/tally/templates/super_admin/home.html:79 +#: tally_ho/apps/tally/templates/super_admin/home.html:92 msgid "Data Entry 1 Clerk" msgstr "كاتب بيانات 1" -#: tally_ho/apps/tally/templates/super_admin/home.html:80 +#: tally_ho/apps/tally/templates/super_admin/home.html:93 msgid "Data Entry 2 Clerk" msgstr "كاتب بيانات 2" -#: tally_ho/apps/tally/templates/super_admin/home.html:82 +#: tally_ho/apps/tally/templates/super_admin/home.html:95 msgid "Quality Control Clerk" msgstr "كاتب مراقبة الجودة" -#: tally_ho/apps/tally/templates/super_admin/home.html:83 +#: tally_ho/apps/tally/templates/super_admin/home.html:96 msgid "Quality Control Supervisor" msgstr "مشرف ضبط الجودة" -#: tally_ho/apps/tally/templates/super_admin/home.html:85 +#: tally_ho/apps/tally/templates/super_admin/home.html:98 msgid "Audit Supervisor" msgstr "مشرف التدقيق" -#: tally_ho/apps/tally/templates/super_admin/home.html:86 +#: tally_ho/apps/tally/templates/super_admin/home.html:99 msgid "Supervisors Approvals" msgstr "تعليق المشرف :" -#: tally_ho/apps/tally/templates/super_admin/home.html:87 +#: tally_ho/apps/tally/templates/super_admin/home.html:100 msgid "Track Corrections" msgstr "تصحيحات" -#: tally_ho/apps/tally/templates/super_admin/home.html:91 +#: tally_ho/apps/tally/templates/super_admin/home.html:104 #: tally_ho/apps/tally/templates/tally_manager/home.html:30 msgid "Users and groups" msgstr "مستخدمين و مجموعات" -#: tally_ho/apps/tally/templates/super_admin/home.html:93 +#: tally_ho/apps/tally/templates/super_admin/home.html:106 #: tally_ho/apps/tally/templates/tally_manager/home.html:32 msgid "Demo groups and passwords (formatted as login/pass):" msgstr "مجموعات تجريبي وكلمات السر (بتنسيق تسجيل الدخول / تمريرة):" @@ -2750,18 +2868,10 @@ msgid "Quarantine checks List" msgstr "فشل في فحص التوقيف" #: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:32 -msgid "Value:" -msgstr "قيمة:" - -#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:39 msgid "Description:" msgstr "وصف:" -#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:46 -msgid "Percentage:" -msgstr "النسبة" - -#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:53 +#: tally_ho/apps/tally/templates/super_admin/quarantine_checks_config.html:39 msgid "Active:" msgstr "نشيط:" @@ -2823,11 +2933,7 @@ msgstr "غلاف الاستمارة" msgid "Office Number:" msgstr "اسم المكتب :" -#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:36 -msgid "Race Type:" -msgstr "نوع التنافس" - -#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:40 +#: tally_ho/apps/tally/templates/super_admin/remove_result_form_confirmation.html:44 msgid "Form State:" msgstr "حالة النمودج" @@ -2868,19 +2974,7 @@ msgstr "محطة" msgid "Downloads" msgstr "Downloads" -#: tally_ho/apps/tally/templates/super_admin/result_export.html:9 -msgid "Result Form List" -msgstr "قائمة النمودج " - #: tally_ho/apps/tally/templates/super_admin/result_export.html:10 -msgid "All Candidate Votes" -msgstr "مرشح" - -#: tally_ho/apps/tally/templates/super_admin/result_export.html:11 -msgid "Active Candidate Votes" -msgstr "مرشح" - -#: tally_ho/apps/tally/templates/super_admin/result_export.html:12 msgid "Result Forms With Duplicate Results" msgstr "متابعة النماذج" @@ -2897,30 +2991,40 @@ msgid "Import progress" msgstr "متابعة النماذج" #: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:9 -msgid "Subconstituencies import progress" -msgstr "تقدم استيراد العناصر الفرعية" +msgid "Ballots import progress" +msgstr "تقدم محطات الاستيراد" #: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:14 -#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:21 -#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:28 -#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:35 -#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:42 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:22 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:30 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:37 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:44 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:51 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:58 msgid "Total elements" msgstr "مجموع العناصر" #: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:17 +msgid "Subconstituencies and Constituencies import progress" +msgstr "تقدم استيراد العناصر الفرعية" + +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:25 +msgid "Subconstituencies Ballots import progress" +msgstr "تقدم استيراد العناصر الفرعية" + +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:33 msgid "Centers import progress" msgstr "تقدم مراكز الاستيراد" -#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:24 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:40 msgid "Stations import progress" msgstr "تقدم محطات الاستيراد" -#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:31 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:47 msgid "Candidates import progress" msgstr "الرقم التسلسلي غير مطابق" -#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:38 +#: tally_ho/apps/tally/templates/tally_manager/batch_progress.html:54 msgid "Result forms import progress" msgstr "نتائج استمارات التصحيح" @@ -3036,312 +3140,292 @@ msgstr "أنشاء التدقيق" msgid "Create Clearance" msgstr "تحقق" -#: tally_ho/apps/tally/views/corrections.py:155 +#: tally_ho/apps/tally/views/corrections.py:142 msgid "There should be exactly two reconciliation results." msgstr "ينبغي أن يكون هناك بالضبط نتيجتين المصالحة." -#: tally_ho/apps/tally/views/corrections.py:212 -#: tally_ho/libs/views/corrections.py:159 +#: tally_ho/apps/tally/views/corrections.py:199 +#: tally_ho/libs/views/corrections.py:154 msgid "Please select correct results for all mis-matched votes." msgstr "يرجى تحديد النتائج الصحيحة لجميع الأصوات غير متطابقة" -#: tally_ho/apps/tally/views/corrections.py:314 +#: tally_ho/apps/tally/views/corrections.py:301 msgid "Results do not match." msgstr "النتائج غير متطابقة" -#: tally_ho/apps/tally/views/corrections.py:435 -#: tally_ho/apps/tally/views/quality_control.py:96 +#: tally_ho/apps/tally/views/corrections.py:418 +#: tally_ho/apps/tally/views/quality_control.py:91 msgid "Quality Control & Archiving" msgstr "ضبط الجودة" -#: tally_ho/apps/tally/views/data/candidate_list_view.py:82 +#: tally_ho/apps/tally/views/data/candidate_list_view.py:83 msgid "Candidate List Per Office" msgstr "مرشح" -#: tally_ho/apps/tally/views/data/form_list_view.py:193 +#: tally_ho/apps/tally/views/data/form_list_view.py:284 #, python-format msgid "Forms for Race %s" msgstr "لم يتم الاستيلام %s" -#: tally_ho/apps/tally/views/data_entry.py:104 +#: tally_ho/apps/tally/views/data_entry.py:111 #, python-format msgid "Return form to %s" msgstr "العودة الى النمودج %s" -#: tally_ho/apps/tally/views/data_entry.py:123 +#: tally_ho/apps/tally/views/data_entry.py:130 msgid "Center and station numbers do not match" msgstr "رقم المركز والمحطة غير متطابقيين" -#: tally_ho/apps/tally/views/data_entry.py:169 +#: tally_ho/apps/tally/views/data_entry.py:176 #, python-format msgid "Data Entry %s" msgstr "مدخل بيانات %s" -#: tally_ho/apps/tally/views/intake.py:27 +#: tally_ho/apps/tally/views/intake.py:26 msgid "Duplicate of a form already entered into system." msgstr "توجد نسخة طبق الاصل من هذ ا النموذج مدخلة بالنظام" -#: tally_ho/apps/tally/views/intake.py:221 +#: tally_ho/apps/tally/views/intake.py:208 msgid "Ballot number do not match for center and form" msgstr "عدد الاقتراع لا تتطابق لمركز وتشكيل" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1493 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1686 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2759 +#: tally_ho/apps/tally/views/quality_control.py:278 +msgid "Archiving" +msgstr "الارشفة" + +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2310 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3385 msgid "Region Constituencies" msgstr "دوائر المنطقة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1878 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2503 msgid "Sub Constituencies votes per candidate" msgstr "أصوات المترشح مفقودة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1889 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2514 msgid "Constituency votes per candidate" msgstr "أصوات المترشح مفقودة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1901 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2526 msgid "Sub Constituency votes per candidate" msgstr "أصوات المترشح مفقودة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1914 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2539 msgid "Sub Constituency candidates list by ballot order" msgstr "قائمة مرشحي الدائرة الانتخابية الفرعية بترتيب الاقتراع" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1932 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2557 msgid "Region Constituencies Progressive Report" msgstr "تقرير مركز الاحصاء" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:1942 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2567 msgid "Region votes per candidate" msgstr "أصوات المترشح مفقودة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2187 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2240 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2258 -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2276 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2813 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2866 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2884 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2902 msgid "{}{}" msgstr "" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2324 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2950 msgid "Sub Constituencies with Station and Centers in Audit" msgstr "الدوائر الانتخابية الفرعية مع المحطة والمراكز في التدقيق" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2341 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2967 msgid "Constituency Centers and Stations in Audit" msgstr "قائمة المركز / المحطة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2357 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2983 msgid "Sub Constituency Stations and Centers in Audit" msgstr "مراكز ومراكز الدوائر الفرعية في المراجعة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2392 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3018 msgid "Region Centers and Stations under process Audit" msgstr "قائمة المركز / المحطة" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:2530 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3156 msgid "Stations and Centers under investigation" msgstr "المحطات والمراكز قيد التحقيق" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3414 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:4061 msgid "All Candidates Votes" msgstr "مرشح" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3415 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:4062 msgid "all_candidates_votes" msgstr "مرشح" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3512 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:4161 msgid "Active Candidates Votes" msgstr "مرشح" -#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:3513 +#: tally_ho/apps/tally/views/reports/administrative_areas_reports.py:4162 msgid "active_candidates_votes" msgstr "مرشح" -#: tally_ho/apps/tally/views/super_admin.py:276 +#: tally_ho/apps/tally/views/super_admin.py:299 msgid "Clearance: New Result Form" msgstr "تحقق : نتائج أستمارة جديدة " -#: tally_ho/apps/tally/views/super_admin.py:311 -#, fuzzy, python-format -#| msgid "Successfully Created form %(form)s" +#: tally_ho/apps/tally/views/super_admin.py:334 +#, python-format msgid "Successfully Created form %(form)s" -msgstr "مركز إزالة بنجاح %(center)s" +msgstr "تم إنشاء النموذج بنجاح %(form)s" -#: tally_ho/apps/tally/views/super_admin.py:332 +#: tally_ho/apps/tally/views/super_admin.py:355 msgid "Form Successfully Updated" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:367 +#: tally_ho/apps/tally/views/super_admin.py:390 msgid "Form Successfully Deleted" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:482 +#: tally_ho/apps/tally/views/super_admin.py:522 msgid "Successfully marked forms as duplicate reviewed" msgstr "نجح وضع علامة على النماذج على أنها مكررة تمت مراجعتها" -#: tally_ho/apps/tally/views/super_admin.py:498 +#: tally_ho/apps/tally/views/super_admin.py:538 msgid "Form successfully sent to clearance" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:507 +#: tally_ho/apps/tally/views/super_admin.py:547 msgid "Archived form can not be sent to clearance." msgstr "لا يمكن إرسال النموذج المؤرشف للتخليص." -#: tally_ho/apps/tally/views/super_admin.py:523 +#: tally_ho/apps/tally/views/super_admin.py:563 #, python-format msgid "Archived form(s) (%s) can not be sent to clearance." msgstr "" -#: tally_ho/apps/tally/views/super_admin.py:528 +#: tally_ho/apps/tally/views/super_admin.py:568 msgid "All forms successfully sent to clearance" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:754 +#: tally_ho/apps/tally/views/super_admin.py:939 #, python-format msgid "Successfully Created Center %(center)s" msgstr "مركز إزالة بنجاح %(center)s" -#: tally_ho/apps/tally/views/super_admin.py:789 -#: tally_ho/apps/tally/views/super_admin.py:828 +#: tally_ho/apps/tally/views/super_admin.py:974 +#: tally_ho/apps/tally/views/super_admin.py:1013 msgid "Center Successfully Removed." msgstr "نقل استمارة ناجحة الى" -#: tally_ho/apps/tally/views/super_admin.py:810 +#: tally_ho/apps/tally/views/super_admin.py:995 #, python-format msgid "Successfully removed center %(center)s" msgstr "مركز إزالة بنجاح %(center)s" -#: tally_ho/apps/tally/views/super_admin.py:859 +#: tally_ho/apps/tally/views/super_admin.py:1044 msgid "This center is tied to 1 or more stations" msgstr "هذا المركز مرتبط بمحطة واحدة أو أكثر" -#: tally_ho/apps/tally/views/super_admin.py:875 +#: tally_ho/apps/tally/views/super_admin.py:1060 msgid "Center Successfully Updated" msgstr "نقل استمارة ناجحة الى" -#: tally_ho/apps/tally/views/super_admin.py:927 -#, fuzzy, python-format -#| msgid "%s Successfully Disabled." +#: tally_ho/apps/tally/views/super_admin.py:1112 msgid "%s Successfully Disabled." msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:946 +#: tally_ho/apps/tally/views/super_admin.py:1131 #, python-format msgid "Successfully disabled center %(center)s" msgstr "مركز المعوقين بنجاح %(center)s" -#: tally_ho/apps/tally/views/super_admin.py:950 +#: tally_ho/apps/tally/views/super_admin.py:1135 #, fuzzy, python-format #| msgid "Successfully disabled station %(station_number)s" msgid "Successfully disabled station %(station_number)s" msgstr "محطة المعوقين بنجاح %(stationNumber)s" -#: tally_ho/apps/tally/views/super_admin.py:975 +#: tally_ho/apps/tally/views/super_admin.py:1160 #, fuzzy, python-format #| msgid "%s Successfully enabled." msgid "%s Successfully enabled." msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:994 -msgid "Race Successfully Created" +#: tally_ho/apps/tally/views/super_admin.py:1179 +msgid "Ballot Successfully Created" +msgstr "نقل استمارة ناجحة الى " + +#: tally_ho/apps/tally/views/super_admin.py:1218 +msgid "Ballot Successfully Updated" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1033 -msgid "Race Successfully Updated" +#: tally_ho/apps/tally/views/super_admin.py:1264 +msgid "Electrol Race Successfully Created" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1092 -msgid "Race Successfully Disabled." +#: tally_ho/apps/tally/views/super_admin.py:1304 +msgid "Electrol Race Successfully Updated" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1108 -msgid "Race Successfully disabled" +#: tally_ho/apps/tally/views/super_admin.py:1372 +msgid "Electrol Race Successfully Disabled." msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1132 -msgid "Race Successfully enabled." +#: tally_ho/apps/tally/views/super_admin.py:1389 +msgid "Electrol Race Successfully disabled" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1146 +#: tally_ho/apps/tally/views/super_admin.py:1412 +msgid "Electrol Race Successfully enabled." +msgstr "نقل استمارة ناجحة الى " + +#: tally_ho/apps/tally/views/super_admin.py:1445 +msgid "Ballot Successfully Disabled." +msgstr "نقل استمارة ناجحة الى " + +#: tally_ho/apps/tally/views/super_admin.py:1462 +msgid "Ballot Successfully disabled" +msgstr "نقل استمارة ناجحة الى " + +#: tally_ho/apps/tally/views/super_admin.py:1486 +msgid "Ballot Successfully enabled." +msgstr "نقل استمارة ناجحة الى " + +#: tally_ho/apps/tally/views/super_admin.py:1500 msgid "Station Successfully Created" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1171 +#: tally_ho/apps/tally/views/super_admin.py:1525 #, python-format msgid "Successfully created station %(center)s" msgstr "مركز إزالة بنجاح %(center)s" -#: tally_ho/apps/tally/views/super_admin.py:1190 -#: tally_ho/apps/tally/views/super_admin.py:1278 +#: tally_ho/apps/tally/views/super_admin.py:1544 +#: tally_ho/apps/tally/views/super_admin.py:1632 msgid "Station Successfully Removed." msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1317 +#: tally_ho/apps/tally/views/super_admin.py:1671 #, python-format msgid "Successfully removed station %(station)s from center %(center)s." msgstr "محطة إزالة بنجاح %(station)s من المركز %(center)s" -#: tally_ho/apps/tally/views/super_admin.py:1344 +#: tally_ho/apps/tally/views/super_admin.py:1698 msgid "Station Successfully Updated" msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1384 +#: tally_ho/apps/tally/views/super_admin.py:1738 msgid "Candidate successfully enabled." msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/super_admin.py:1404 +#: tally_ho/apps/tally/views/super_admin.py:1758 msgid "Candidate successfully disabled." msgstr "نقل استمارة ناجحة الى " -#: tally_ho/apps/tally/views/tally_manager.py:150 -msgid "Invalid ballot order file" -msgstr "ملف طلب الاقتراع غير صحيح" - -#: tally_ho/apps/tally/views/tally_manager.py:168 -#, python-format -msgid "Column %(cols)s is missing in the sub constituency file" -msgid_plural "Columns %(cols)s are missing in the sub constituency file" -msgstr[0] "مفقود في ملف الدائرة الفرعية %(cols)s عمود" -msgstr[1] "مفقودة في ملف الدائرة الفرعية %(cols)s الأعمدة" - -#: tally_ho/apps/tally/views/tally_manager.py:181 -#, python-format -msgid "Column %(cols)s is missing in the centers file" -msgid_plural "Columns %(cols)s are missing in the centers file" -msgstr[0] "مفقود في ملف المراكز %(cols)s عمود" -msgstr[1] "مفقودة في ملف المراكز %(cols)s الأعمدة" - -#: tally_ho/apps/tally/views/tally_manager.py:192 -#, python-format -msgid "Column %(cols)s is missing in the stations file" -msgid_plural "Columns %(cols)s are missing in the stations file" -msgstr[0] "مفقود في ملف المحطات %(cols)s عمود" -msgstr[1] "مفقودة في ملف المحطات %(cols)s الأعمدة" - -#: tally_ho/apps/tally/views/tally_manager.py:203 -#, python-format -msgid "Column %(cols)s is missing in the candidates file" -msgid_plural "Columns %(cols)s are missing in the candidates file" -msgstr[0] "مفقود في ملف المرشحين %(cols)s عمود" -msgstr[1] "مفقودة في ملف المرشحين %(cols)s الأعمدة" - -#: tally_ho/apps/tally/views/tally_manager.py:214 -#, python-format -msgid "Column %(cols)s is missing in the result form file" -msgid_plural "Columns %(cols)s are missing in the result form file" -msgstr[0] "مفقود في ملف نموذج النتيجة %(cols)s عمود" -msgstr[1] "مفقودة في ملف نموذج النتيجة %(cols)s الأعمدة" - -#: tally_ho/apps/tally/views/tally_manager.py:231 -#: tally_ho/apps/tally/views/tally_manager.py:239 +#: tally_ho/apps/tally/views/tally_manager.py:187 msgid "{}" msgstr "" -#: tally_ho/apps/tally/views/tally_manager.py:657 +#: tally_ho/apps/tally/views/tally_manager.py:716 #, python-format msgid "Successfully set user timeout to %(user_idle_timeout)s minutes" -msgstr "" +msgstr "تم ضبط مهلة المستخدم بنجاح إلى %(user_idle_timeout)s دقيقة" #: tally_ho/libs/models/dependencies.py:11 msgid "Results exist for barcodes:" @@ -3419,19 +3503,15 @@ msgstr "استبعد بسبب قرار المحكمة" msgid "Materials destroyed" msgstr "المواد دمرت" -#: tally_ho/libs/models/enums/gender.py:14 -msgid "Unisex" -msgstr "للجنسين" - #: tally_ho/libs/reports/progress.py:105 tally_ho/libs/reports/progress.py:126 msgid "No results" msgstr "لا توجد نتائج" -#: tally_ho/libs/reports/progress.py:153 +#: tally_ho/libs/reports/progress.py:154 msgid "Expected" msgstr "مترقب" -#: tally_ho/libs/reports/progress.py:254 +#: tally_ho/libs/reports/progress.py:255 msgid "Not Received" msgstr "لم يتم الاستيلام" @@ -3504,7 +3584,7 @@ msgstr "كلمة المرور(مجدداً)" msgid "Change my password" msgstr "تغيير كلمة itالمرور خاصتي" -#: tally_ho/libs/utils/active_status.py:96 +#: tally_ho/libs/utils/active_status.py:124 msgid "Candidate does not exist" msgstr "الرقم التسلسلي غير مطابق" @@ -3524,15 +3604,15 @@ msgstr "محطة" msgid "Enable Station" msgstr "محطة" -#: tally_ho/libs/utils/templates.py:84 +#: tally_ho/libs/utils/templates.py:100 msgid "Admin View" msgstr "عرض اجراء الاستمارة" -#: tally_ho/libs/views/exports.py:441 +#: tally_ho/libs/views/exports.py:438 msgid "File Not found!" msgstr "الصفحة غير موجودة" -#: tally_ho/libs/views/exports.py:444 +#: tally_ho/libs/views/exports.py:441 msgid "Report not found." msgstr "الصفحة غير موجودة" @@ -3549,6 +3629,205 @@ msgstr "خطأ : بيانات ناقصة " msgid "Session result_form does not match submitted data." msgstr "البيانات الناتجة غير مطابقه للنمودج المداخل" +#~ msgid "Race for ballot is disabled" +#~ msgstr "اسم المركز" + +#~ msgid "" +#~ "Check that the centers and stations assigned to result forms in the " +#~ "system match those in the raw data." +#~ msgstr "" +#~ "تأكد من أن المراكز والمحطات المخصصة لينتج أشكال في النظام, تطابق تلك " +#~ "الموجودة في البيانات الخام." + +#~ msgid "Import polling data." +#~ msgstr "احضار بيانات الاقتراع" + +#~ msgid "General" +#~ msgstr "عام" + +#~ msgid "Women" +#~ msgstr "نساء" + +#~ msgid "Component Amazigh" +#~ msgstr "مكون الأمازيغ" + +#~ msgid "Component Twarag" +#~ msgstr "مكون الطوارق" + +#~ msgid "Component Tebu" +#~ msgstr "مكون التبو" + +#~ msgid "Presidential" +#~ msgstr "رئاسي" + +#~ msgid "Number of signatures in the VR" +#~ msgstr "عدد التواقيع فى سجل الناخبين" + +#~ msgid "Number of blank ballots" +#~ msgstr "عدد أوراق الاقتراع الغير مستخدمة" + +#~ msgid "Undefined" +#~ msgstr "غير معروف" + +#~ msgid "Form Type:" +#~ msgstr "نوع الاستمارة :" + +#~ msgid "Form Race Type:" +#~ msgstr "نوع استمارة التنافس" + +#~ msgid "Voting district" +#~ msgstr "دائرة انتخابيه " + +#~ msgid "Export Presidential Forms in JSON" +#~ msgstr "تصدير النماذج الرئاسية بتنسيق JSON" + +#~ msgid "Export Parliamentary Forms in JSON" +#~ msgstr "تصدير النماذج البرلمانية في JSON" + +#~ msgid "New Race" +#~ msgstr "لكل مكتب" + +#~ msgid "Race Attachments" +#~ msgstr "مرفقات العرق" + +#~ msgid "Percent Complete" +#~ msgstr "استعراض مكتمل" + +#~ msgid "Page" +#~ msgstr "صفحة" + +#~ msgid "of" +#~ msgstr "ل" + +#~ msgid "Serial number of ballots received by the polling station" +#~ msgstr "الارقام التسلسية لاوراق الاقتراع التى تم استلامها من محطة الاقتراع" + +#~ msgid "section (d) to be filled after the completion of the results forms" +#~ msgstr "القسم (ج) يعبأ بعد عد اوراق الاقتراع المستخرجة من صندوق الاقتراع" + +#~ msgid "Number of blank ballots:" +#~ msgstr "عدد أوراق الاقتراع الغير مستخدمة:" + +#~ msgid "" +#~ "If after recounting, the difference persists, the ballots of the fields " +#~ "9, 10 and 11 should be recounted as in the manual." +#~ msgstr "" +#~ "إذا،ظل الفرق قائما بعد إعادة الحساب ، يجب اعادة عد اوراق الاقتراع فى " +#~ "الخانات 9 و 10 و 11 كما مبين فى الدليل." + +#~ msgid "" +#~ "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." +#~ msgstr "" +#~ "إذا ظل الفرق، اكتب ملاحظة فى الخانة أسفل الاستمارة ذاكرا الإجراءات " +#~ "المتبعة قبل الانتقال للخطوة التالية." + +#~ msgid "Section (B) to be filled after closure of polling station" +#~ msgstr "القسم (ب) يعبأ عند غلق المحطة" + +#~ msgid "Number of signatures in the VR:" +#~ msgstr "عدد التواقيع فى سجل الناخبين:" + +#~ msgid "Form Results" +#~ msgstr "لا توجد نتائج" + +#~ msgid "Filter out Centers and Stations from results." +#~ msgstr "قائمة المركز / المحطة" + +#~ msgid "Invalid Ballots" +#~ msgstr "أوراق الاقتراع الباطلة" + +#~ msgid "Unstamped Ballots" +#~ msgstr "عدد الأوراق الغير مختومة" + +#~ msgid "Cancelled Ballots" +#~ msgstr "عدد أوراق الاقتراع الملغاة" + +#~ msgid "Spoilt Ballots" +#~ msgstr "الرقم التسلسلي" + +#~ msgid "Unused Ballots" +#~ msgstr "عدد أوراق الاقتراع الغير مستخدمة" + +#~ msgid "Number of Signatures" +#~ msgstr "عدد التواقيع فى سجل الناخبين" + +#~ msgid "Received Ballots Papers" +#~ msgstr "أوراق الاقتراع المستلمة" + +#~ msgid " Regions Turn Out Report" +#~ msgstr "تقرير إطفاء المناطق" + +#~ msgid " Region Turn Out Report" +#~ msgstr "تقرير اغلاق المنطقة" + +#~ msgid " Constituency Turn Out Report" +#~ msgstr "الدائرة الانتخابية تقرير اغلاق" + +#~ msgid "Total voters" +#~ msgstr "مجموع المصوتين" + +#~ msgid "Voters voted" +#~ msgstr "صوت الناخبون" + +#~ msgid "Male voters" +#~ msgstr "الناخبون الذكور" + +#~ msgid "Female voters" +#~ msgstr "انثي" + +#~ msgid "Turnout percentage" +#~ msgstr "النسبة" + +#~ msgid "Reports Races" +#~ msgstr "تقارير" + +#~ msgid "Value:" +#~ msgstr "قيمة:" + +#~ msgid "Percentage:" +#~ msgstr "النسبة" + +#~ msgid "Result Form List" +#~ msgstr "قائمة النمودج " + +#~ msgid "All Candidate Votes" +#~ msgstr "مرشح" + +#~ msgid "Active Candidate Votes" +#~ msgstr "مرشح" + +#~ msgid "Invalid ballot order file" +#~ msgstr "ملف طلب الاقتراع غير صحيح" + +#, python-format +#~ msgid "Column %(cols)s is missing in the sub constituency file" +#~ msgid_plural "Columns %(cols)s are missing in the sub constituency file" +#~ msgstr[0] "مفقود في ملف الدائرة الفرعية %(cols)s عمود" +#~ msgstr[1] "مفقودة في ملف الدائرة الفرعية %(cols)s الأعمدة" + +#, python-format +#~ msgid "Column %(cols)s is missing in the stations file" +#~ msgid_plural "Columns %(cols)s are missing in the stations file" +#~ msgstr[0] "مفقود في ملف المحطات %(cols)s عمود" +#~ msgstr[1] "مفقودة في ملف المحطات %(cols)s الأعمدة" + +#, python-format +#~ msgid "Column %(cols)s is missing in the candidates file" +#~ msgid_plural "Columns %(cols)s are missing in the candidates file" +#~ msgstr[0] "مفقود في ملف المرشحين %(cols)s عمود" +#~ msgstr[1] "مفقودة في ملف المرشحين %(cols)s الأعمدة" + +#, python-format +#~ msgid "Column %(cols)s is missing in the result form file" +#~ msgid_plural "Columns %(cols)s are missing in the result form file" +#~ msgstr[0] "مفقود في ملف نموذج النتيجة %(cols)s عمود" +#~ msgstr[1] "مفقودة في ملف نموذج النتيجة %(cols)s الأعمدة" + +#~ msgid "Unisex" +#~ msgstr "للجنسين" + #~ msgid "No data available in table" #~ msgstr "لا توجد بيانات متوفرة في الجدول" @@ -3600,9 +3879,6 @@ msgstr "البيانات الناتجة غير مطابقه للنمودج ال #~ msgid "Abort" #~ msgstr "الغاء وخروج" -#~ msgid "Station Status" -#~ msgstr "محطة" - #~ msgid "OK" #~ msgstr "تمت العملية بنجاح" @@ -3624,11 +3900,9 @@ msgstr "البيانات الناتجة غير مطابقه للنمودج ال #~ msgid "Implement Recommendation" #~ msgstr "قم بتطبيق توصية" -#, fuzzy #~ msgid "Form Clearance Pending List" #~ msgstr "قائمة التصحيح" -#, fuzzy #~ msgid "Form Clearance Pendings" #~ msgstr "قائمة التصحيح" diff --git a/tally_ho/apps/tally/forms/recon_form.py b/tally_ho/apps/tally/forms/recon_form.py index bddcd9b96..6ce23cc9b 100644 --- a/tally_ho/apps/tally/forms/recon_form.py +++ b/tally_ho/apps/tally/forms/recon_form.py @@ -1,5 +1,7 @@ +from django import forms from django.forms import ModelForm from tally_ho.apps.tally.models import ReconciliationForm +from django.utils.translation import gettext_lazy as _ disable_copy_input = { @@ -15,30 +17,30 @@ class ReconForm(ModelForm): class Meta: model = ReconciliationForm - fields = localized_fields = ['ballot_number_from', - 'ballot_number_to', - 'is_stamped', - 'number_ballots_received', - 'number_signatures_in_vr', - 'number_unused_ballots', - 'number_spoiled_ballots', - 'number_cancelled_ballots', - 'number_ballots_outside_box', - 'number_ballots_inside_box', - 'number_ballots_inside_and_outside_box', - 'number_unstamped_ballots', - 'number_invalid_votes', - 'number_valid_votes', - 'number_sorted_and_counted', - 'signature_polling_officer_1', - 'signature_polling_officer_2', - 'signature_polling_station_chair', - 'signature_dated'] + fields = localized_fields =\ + ['is_stamped', + 'number_ballots_received', + 'number_of_voter_cards_in_the_ballot_box', + 'number_unused_ballots', + 'number_spoiled_ballots', + 'number_cancelled_ballots', + 'number_ballots_outside_box', + 'number_ballots_inside_box', + 'number_ballots_inside_and_outside_box', + 'total_of_cancelled_ballots_and_ballots_inside_box', + 'number_unstamped_ballots', + 'number_invalid_votes', + 'number_valid_votes', + 'number_sorted_and_counted', + 'signature_polling_officer_1', + 'signature_polling_officer_2', + 'signature_polling_station_chair', + 'signature_dated', + 'notes'] localized_fields = '__all__' def __init__(self, *args, **kwargs): super(ReconForm, self).__init__(*args, **kwargs) - self.fields['ballot_number_from'].widget.attrs['autofocus'] = 'on' for field in self.fields: for k, v in list(disable_copy_input.items()): @@ -52,3 +54,23 @@ def __init__(self, *args, **kwargs): class_str, self.fields[field].widget.attrs.get('class')) self.fields[field].widget.attrs['class'] = class_str + def clean(self): + """Verify that the total of field number_cancelled_ballots and + field number_ballots_inside_box match the value of field + total_of_cancelled_ballots_and_ballots_inside_box + """ + if self.is_valid(): + cleaned_data = super(ReconForm, self).clean() + number_cancelled_ballots =\ + cleaned_data.get('number_cancelled_ballots') + number_ballots_inside_box =\ + cleaned_data.get('number_ballots_inside_box') + total_of_cancelled_ballots_and_ballots_inside_box =\ + cleaned_data.get( + 'total_of_cancelled_ballots_and_ballots_inside_box') + + if (number_cancelled_ballots + number_ballots_inside_box) !=\ + total_of_cancelled_ballots_and_ballots_inside_box: + raise forms.ValidationError( + _('Total of fied 5 and 7 is incorrect')) + return cleaned_data diff --git a/tally_ho/apps/tally/management/commands/import_centers.py b/tally_ho/apps/tally/management/commands/import_centers.py index db22a539b..5df990476 100644 --- a/tally_ho/apps/tally/management/commands/import_centers.py +++ b/tally_ho/apps/tally/management/commands/import_centers.py @@ -224,7 +224,8 @@ def create_centers_from_centers_file_data( constituency = get_constituency_by_name( field_val, constituencies_by_name, - kwargs.get('sub_constituency') + kwargs.get('sub_constituency'), + tally, ) kwargs['constituency'] = constituency continue diff --git a/tally_ho/apps/tally/management/commands/import_result_forms.py b/tally_ho/apps/tally/management/commands/import_result_forms.py index 5f821ca2b..f4946650a 100644 --- a/tally_ho/apps/tally/management/commands/import_result_forms.py +++ b/tally_ho/apps/tally/management/commands/import_result_forms.py @@ -6,6 +6,7 @@ build_generic_model_key_values_from_duckdb_row_tuple_data, check_duplicates, check_for_missing_columns, + find_missing_center_codes, get_ballot_by_ballot_number, get_office_by_office_name_and_region_name, ) @@ -200,6 +201,15 @@ def async_import_results_forms_from_result_forms_file( csv_file_path=file_path, field='barcode' ) + centers_by_code =\ + { + center.code:\ + center for center in Center.objects.filter(tally=tally) + } + find_missing_center_codes( + result_forms_file=file_path, + centers_by_code=centers_by_code + ) stations_by_number_underscore_center_code =\ { @@ -211,11 +221,6 @@ def async_import_results_forms_from_result_forms_file( ballot.number:\ ballot for ballot in Ballot.objects.filter(tally=tally) } - centers_by_code =\ - { - center.code:\ - center for center in Center.objects.filter(tally=tally) - } offices_by_name_underscore_region_name =\ { f'{office.name}_{office.region.name}':\ diff --git a/tally_ho/apps/tally/management/commands/utils.py b/tally_ho/apps/tally/management/commands/utils.py index f68686c2a..0f85a7503 100644 --- a/tally_ho/apps/tally/management/commands/utils.py +++ b/tally_ho/apps/tally/management/commands/utils.py @@ -112,6 +112,7 @@ def get_constituency_by_name( constituency_name, constituency_by_name, sc_code, + tally, ): constituency = None if constituency_name != NO_CONSTITUENCY and\ @@ -119,8 +120,11 @@ def get_constituency_by_name( constituency =\ constituency_by_name.get(constituency_name) if constituency is None: - raise Constituency.DoesNotExist( - f'Constituency {constituency_name} does not exist') + constituency_obj, _ = Constituency.objects.get_or_create( + name=constituency_name, + tally=tally, + ) + constituency = constituency_obj return constituency @@ -221,6 +225,19 @@ def check_for_missing_columns( raise Exception(error_message) return None +class DuplicatesFoundError(Exception): + """Exception raised when duplicates are found in a CSV file based on + a specified field.""" + def __init__(self, field, duplicates): + self.field = field + self.duplicates = duplicates + message = ( + f"Duplicate entries detected in the '{field}' column: " + f"{', '.join(map(str, duplicates))}." + "Please ensure these values are unique." + ) + super().__init__(message) + def check_duplicates(csv_file_path: str, field: str) -> None: """ Checks for duplicates in a CSV file based on a specified field using @@ -252,11 +269,51 @@ def check_duplicates(csv_file_path: str, field: str) -> None: query = """ SELECT {field}, COUNT(*) AS cnt FROM read_csv_auto(?) + WHERE {field} IS NOT NULL GROUP BY {field} HAVING cnt > 1 """.format(field=field) result = con.execute(query, [csv_file_path]).fetchall() + con.close() if len(result) > 0: - raise Exception(f"Duplicates found for field '{field}'") + duplicate_values = [row[0] for row in result] + raise DuplicatesFoundError(field, duplicate_values) + + +class CenterCodeNotFoundException(Exception): + def __init__(self, center_codes): + self.center_codes = center_codes + self.message = ( + "The following center codes do not exist in the centers file: " + f"{', '.join(map(str, center_codes))}" + ) + super().__init__(self.message) + +def find_missing_center_codes(result_forms_file, centers_by_code): + center_codes_list = list(centers_by_code.keys()) + + con = duckdb.connect() + + center_codes_tuple = tuple(center_codes_list) + + query = f""" + SELECT rf.center_code + FROM read_csv_auto('{result_forms_file}') AS rf + WHERE rf.center_code NOT IN {center_codes_tuple}; + """ + + # Execute the query and fetch the result + result = con.execute(query).fetchall() + + # Close the connection + con.close() + + # Extract the missing center codes + missing_center_codes = [row[0] for row in result] + + # Raise the exception if there are any missing center codes + if missing_center_codes: + print(f"center codes: {missing_center_codes}") + raise CenterCodeNotFoundException(missing_center_codes) diff --git a/tally_ho/apps/tally/migrations/0053_reconciliationform_number_of_voter_cards_in_the_ballot_box.py b/tally_ho/apps/tally/migrations/0053_reconciliationform_number_of_voter_cards_in_the_ballot_box.py new file mode 100644 index 000000000..a372ca137 --- /dev/null +++ b/tally_ho/apps/tally/migrations/0053_reconciliationform_number_of_voter_cards_in_the_ballot_box.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2024-10-23 08:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tally', '0052_remove_reconciliationform_number_blank_ballots'), + ] + + operations = [ + migrations.AddField( + model_name='reconciliationform', + name='number_of_voter_cards_in_the_ballot_box', + field=models.PositiveIntegerField(default=0, verbose_name='Number of voter cards in the ballot box'), + ), + ] diff --git a/tally_ho/apps/tally/migrations/0054_remove_reconciliationform_number_signatures_in_vr.py b/tally_ho/apps/tally/migrations/0054_remove_reconciliationform_number_signatures_in_vr.py new file mode 100644 index 000000000..c0869fe46 --- /dev/null +++ b/tally_ho/apps/tally/migrations/0054_remove_reconciliationform_number_signatures_in_vr.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.2 on 2024-10-23 08:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tally', '0053_reconciliationform_number_of_voter_cards_in_the_ballot_box'), + ] + + operations = [ + migrations.RemoveField( + model_name='reconciliationform', + name='number_signatures_in_vr', + ), + ] diff --git a/tally_ho/apps/tally/migrations/0055_reconciliationform_total_of_cancelled_ballots_and_ballots_inside_box.py b/tally_ho/apps/tally/migrations/0055_reconciliationform_total_of_cancelled_ballots_and_ballots_inside_box.py new file mode 100644 index 000000000..5b3a488fd --- /dev/null +++ b/tally_ho/apps/tally/migrations/0055_reconciliationform_total_of_cancelled_ballots_and_ballots_inside_box.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2024-10-24 07:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tally', '0054_remove_reconciliationform_number_signatures_in_vr'), + ] + + operations = [ + migrations.AddField( + model_name='reconciliationform', + name='total_of_cancelled_ballots_and_ballots_inside_box', + field=models.PositiveIntegerField(default=0, verbose_name='Total of fields 5+7'), + ), + ] diff --git a/tally_ho/apps/tally/migrations/0056_reconciliationform_notes.py b/tally_ho/apps/tally/migrations/0056_reconciliationform_notes.py new file mode 100644 index 000000000..fdece1079 --- /dev/null +++ b/tally_ho/apps/tally/migrations/0056_reconciliationform_notes.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2024-10-25 12:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tally', '0055_reconciliationform_total_of_cancelled_ballots_and_ballots_inside_box'), + ] + + operations = [ + migrations.AddField( + model_name='reconciliationform', + name='notes', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/tally_ho/apps/tally/migrations/0057_alter_reconciliationform_ballot_number_from_and_more.py b/tally_ho/apps/tally/migrations/0057_alter_reconciliationform_ballot_number_from_and_more.py new file mode 100644 index 000000000..02cb26104 --- /dev/null +++ b/tally_ho/apps/tally/migrations/0057_alter_reconciliationform_ballot_number_from_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.2 on 2024-10-26 15:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tally', '0056_reconciliationform_notes'), + ] + + operations = [ + migrations.AlterField( + model_name='reconciliationform', + name='ballot_number_from', + field=models.CharField(max_length=256, null=True, verbose_name='from:'), + ), + migrations.AlterField( + model_name='reconciliationform', + name='ballot_number_to', + field=models.CharField(max_length=256, null=True, verbose_name='to:'), + ), + ] diff --git a/tally_ho/apps/tally/models/reconciliation_form.py b/tally_ho/apps/tally/models/reconciliation_form.py index 7613a02bf..d07da76d7 100644 --- a/tally_ho/apps/tally/models/reconciliation_form.py +++ b/tally_ho/apps/tally/models/reconciliation_form.py @@ -45,13 +45,14 @@ class Meta: active = models.BooleanField(default=True) entry_version = EnumIntegerField(EntryVersion) - ballot_number_from = models.CharField(_('from:'), max_length=256) - ballot_number_to = models.CharField(_('to:'), max_length=256) + ballot_number_from = models.CharField( + _('from:'), max_length=256, null=True) + ballot_number_to = models.CharField(_('to:'), max_length=256, null=True) is_stamped = models.BooleanField(_('Is the form stamped?')) number_ballots_received = models.PositiveIntegerField( _('Total number of ballots received by the polling station')) - number_signatures_in_vr = models.PositiveIntegerField( - _('Number of signatures in the VR')) + number_of_voter_cards_in_the_ballot_box = models.PositiveIntegerField( + _('Number of voter cards in the ballot box'), default=0) number_unused_ballots = models.PositiveIntegerField( _('Number of unused ballots')) number_spoiled_ballots = models.PositiveIntegerField( @@ -64,6 +65,8 @@ class Meta: _('Number of ballots found inside the ballot box')) number_ballots_inside_and_outside_box = models.PositiveIntegerField( _('Total number of ballots found inside and outside the ballot box')) + total_of_cancelled_ballots_and_ballots_inside_box =\ + models.PositiveIntegerField(_('Total of fields 5+7'), default=0) number_unstamped_ballots = models.PositiveIntegerField( _('Number of unstamped ballots')) number_invalid_votes = models.PositiveIntegerField( @@ -79,6 +82,7 @@ class Meta: signature_polling_station_chair = models.BooleanField( _('Is the form signed by the polling station chair?')) signature_dated = models.BooleanField(_('Is the form dated?')) + notes = models.TextField(null=True, blank=True) objects = ReconciliationFormSet.as_manager() @property diff --git a/tally_ho/apps/tally/static/css/default.css b/tally_ho/apps/tally/static/css/default.css index 0e06b6a66..906d40829 100755 --- a/tally_ho/apps/tally/static/css/default.css +++ b/tally_ho/apps/tally/static/css/default.css @@ -83,20 +83,30 @@ form{ margin-top:1em; padding-top:.6em; } -.blue-bg{ +.blue-bg { background:#AFCCE5; + border-color: light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); +} + +.blue-bg .form-control:focus { + background:#AFCCE5; + border-color: light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); + box-shadow: light-dark(rgb(118, 118, 118), rgb(133, 133, 133)); } .gray-bg{ background:#D1D3D4; border:1px solid #808080; min-height:4em; - padding:.4em; + padding:1.4em; text-align:center; vertical-align:middle!important; } .pink-bg{ background:#FFDFDD; } +.green-bg{ + background: linear-gradient(to right, #e2f0d9 0%, #e2f0d9 100%); +} .yellow-bg{ background:#FFFFC2; } diff --git a/tally_ho/apps/tally/static/js/barcode_verify.js b/tally_ho/apps/tally/static/js/barcode_verify.js index f187412c4..2ac8988d3 100644 --- a/tally_ho/apps/tally/static/js/barcode_verify.js +++ b/tally_ho/apps/tally/static/js/barcode_verify.js @@ -1,64 +1,44 @@ -function barcodes_match(empty_message, length_message, mismatch_message) { - const barcode = document.getElementById("id_barcode"); - const barcode_copy = document.getElementById("id_barcode_copy"); - const barcode_scan_entry = document.getElementById("barcode_scan_entry").hidden; - if (barcode_scan_entry){ - if (barcode.value === barcode_copy.value) { - barcode_copy.parentNode.setAttribute("class", "has-success"); - return true; - } - barcode_copy.parentNode.setAttribute("class", "has-error"); +let barcodeNumber = ""; - if (barcode.value === "" || barcode_copy.value === "") { - barcode.parentNode.setAttribute("class", "has-error"); - alert(empty_message); - } else if (barcode.value.length < 1) { - barcode.parentNode.setAttribute("class", "has-error"); - alert(length_message); - } - else { - alert(mismatch_message); - } - } +const barcode_scanner = (barcodeInputField) => { + let typingTimeout; - return; -} - -let barcode_number = ""; + if (barcodeInputField) { + barcodeInputField.addEventListener("keypress", function (e) { + // Reset the timeout on each keypress + clearTimeout(typingTimeout); -const barcode_scanner = (barcode_input_field) => { - if (barcode_input_field) { - barcode_input_field.addEventListener("keydown", function (e) { - e.preventDefault(); - let textInput = e.key; - if (!(isNaN(parseInt(textInput))) && e.code === "KeyA") { - barcode_number = barcode_number + textInput; - } else if (barcode_number && (e.keyCode === 13)) { - barcode_input_field.value = barcode_number; - barcode_input_field.readOnly = true; + if (e.key === "Enter") { + barcodeInputField.value = barcodeNumber; + barcodeInputField.readOnly = true; + barcodeNumber = ""; // Reset after a full scan is detected + } else if (!isNaN(e.key)) { + barcodeNumber += e.key; // Append numeric input } - }); - } -} -document.onreadystatechange = function () { - if (document.readyState === "interactive") { - const barcode_scan_input_field = document.getElementById("id_scanned_barcode"); - barcode_scan_input_field.value = ""; - } - else if (document.readyState === "complete") { - const barcode_scan_input_field = document.getElementById("id_scanned_barcode"); - const barcode_manual_entry_input_field = document.getElementById("id_barcode"); - const barcode_copy_manual_entry_input_field = document.getElementById("id_barcode_copy"); - barcode_number = ""; - barcode_scan_input_field.setAttribute("required", "true"); - barcode_manual_entry_input_field.removeAttribute("required"); - barcode_copy_manual_entry_input_field.removeAttribute("required"); - barcode_scanner(barcode_scan_input_field); + // Set a timeout to reset if no key is pressed in 200ms (example debounce time) + typingTimeout = setTimeout(() => { + barcodeNumber = ""; + }, 200); + }); } }; +document.addEventListener("DOMContentLoaded", () => { + const barcodeScanInputField = document.getElementById("id_scanned_barcode"); + const barcodeManualEntryInputField = document.getElementById("id_barcode"); + const barcodeCopyManualEntryInputField = document.getElementById("id_barcode_copy"); + + barcodeNumber = ""; + barcode_scanner(barcodeScanInputField); + + // Set or remove required attributes based on form mode + barcodeScanInputField.setAttribute("required", "true"); + barcodeManualEntryInputField.removeAttribute("required"); + barcodeCopyManualEntryInputField.removeAttribute("required"); +}); +// Utility functions for showing and hiding placeholders function show_barcode_hide_placeholder() { $("#barcode_placeholder").hide(); $("#id_barcode").show(); @@ -69,82 +49,62 @@ function hide_barcode_show_placeholder() { $("#id_barcode").hide(); } -const input_validation = () => $(document).ready(function () { - $("#id_barcode").focusout(function (evt) { - if ($("#id_barcode").val() !== "" && $("#id_barcode_copy").val() !== $("#id_barcode").val()) { +// Enhanced input validation +function input_validation() { + $("#id_barcode").on("focusout", () => { + if ($("#id_barcode").val() && $("#id_barcode_copy").val() !== $("#id_barcode").val()) { hide_barcode_show_placeholder(); } }); - $("#barcode_placeholder").focusin(function (evt) { - show_barcode_hide_placeholder(); - $("#id_barcode").focus(); - }); - - $("#id_barcode_copy").focusout(function (evt) { - if ($("#id_barcode_copy").val().length === $("#id_barcode").val().length && - $("#id_barcode").val() != $("#id_barcode_copy").val()) { - show_barcode_hide_placeholder(); - } - }); - - $("#id_barcode_copy").keyup(function (evt) { + $("#id_barcode_copy").on("focusout keyup", () => { if ($("#id_barcode_copy").val().length === $("#id_barcode").val().length && $("#id_barcode").val() === $("#id_barcode_copy").val()) { show_barcode_hide_placeholder(); - $("#id_barcode").parent().addClass("has-success"); - $("#id_barcode_copy").parent().addClass("has-success"); + $("#id_barcode, #id_barcode_copy").parent().addClass("has-success"); } else if ($("#id_barcode_copy").val().length === $("#id_barcode").val().length) { show_barcode_hide_placeholder(); - $("#id_barcode").parent().removeClass("has-success"); - $("#id_barcode_copy").parent().removeClass("has-success"); - $("#id_barcode").parent().addClass("has-error"); - $("#id_barcode_copy").parent().addClass("has-error"); + $("#id_barcode, #id_barcode_copy").parent().removeClass("has-success").addClass("has-error"); } else { hide_barcode_show_placeholder(); } }); +} - $("#id_barcode_copy").focusin(function (evt) { - hide_barcode_show_placeholder(); - }); -}); - -function change_barcode_entry_mode(barcode_entry_mode) { - const id_form_instructions = document.getElementById("id_form_instructions"); - const barcode_manual_entry = document.getElementById("barcode_manual_entry"); - const barcode_scan_entry = document.getElementById("barcode_scan_entry"); - const barcode_scan_input_field = document.getElementById("id_scanned_barcode"); - const barcode_manual_entry_input_field = document.getElementById("id_barcode"); - const barcode_copy_manual_entry_input_field = document.getElementById("id_barcode_copy"); - const manual_entry_button = document.getElementById("manual_entry_button"); - const scanned_entry_button = document.getElementById("scanned_entry_button"); - const barcode_copy_manual_entry = document.getElementById("barcode_copy_manual_entry"); +// Toggle between barcode entry modes +function change_barcode_entry_mode(barcodeEntryMode) { + const idFormInstructions = document.getElementById("id_form_instructions"); + const barcodeManualEntry = document.getElementById("barcode_manual_entry"); + const barcodeScanEntry = document.getElementById("barcode_scan_entry"); + const barcodeScanInputField = document.getElementById("id_scanned_barcode"); + const barcodeManualEntryInputField = document.getElementById("id_barcode"); + const barcodeCopyManualEntryInputField = document.getElementById("id_barcode_copy"); + const manualEntryButton = document.getElementById("manual_entry_button"); + const scannedEntryButton = document.getElementById("scanned_entry_button"); + const barcodeCopyManualEntry = document.getElementById("barcode_copy_manual_entry"); - if (barcode_entry_mode === "manual") { - id_form_instructions.innerText = "Enter Barcode"; - barcode_scan_entry.setAttribute("hidden", "true"); - barcode_manual_entry.removeAttribute("hidden"); - barcode_manual_entry_input_field.focus(); - barcode_manual_entry_input_field.setAttribute("required", "true"); - barcode_copy_manual_entry_input_field.setAttribute("required", "true"); - barcode_scan_input_field.removeAttribute("required"); - barcode_copy_manual_entry.removeAttribute("hidden"); - manual_entry_button.setAttribute("hidden", "true") - scanned_entry_button.removeAttribute("hidden"); + if (barcodeEntryMode === "manual") { + idFormInstructions.innerText = "Enter Barcode"; + barcodeScanEntry.hidden = true; + barcodeManualEntry.hidden = false; + barcodeManualEntryInputField.required = true; + barcodeCopyManualEntryInputField.required = true; + barcodeScanInputField.removeAttribute("required"); + barcodeCopyManualEntry.hidden = false; + manualEntryButton.hidden = true; + scannedEntryButton.hidden = false; input_validation(); - } - else if (barcode_entry_mode === "scan") { - id_form_instructions.innerText = "Scan Barcode to proceed"; - barcode_manual_entry.setAttribute("hidden", "true"); - barcode_copy_manual_entry.setAttribute("hidden", "true"); - barcode_scan_entry.removeAttribute("hidden"); - barcode_scan_input_field.focus(); - barcode_scan_input_field.setAttribute("required", "true"); - barcode_manual_entry_input_field.removeAttribute("required"); - barcode_copy_manual_entry_input_field.removeAttribute("required"); - scanned_entry_button.setAttribute("hidden", "true"); - manual_entry_button.removeAttribute("hidden"); - barcode_scanner(barcode_scan_input_field); + } else if (barcodeEntryMode === "scan") { + idFormInstructions.innerText = "Scan Barcode to proceed"; + barcodeManualEntry.hidden = true; + barcodeCopyManualEntry.hidden = true; + barcodeScanEntry.hidden = false; + barcodeScanInputField.focus(); + barcodeScanInputField.required = true; + barcodeManualEntryInputField.removeAttribute("required"); + barcodeCopyManualEntryInputField.removeAttribute("required"); + manualEntryButton.hidden = false; + scannedEntryButton.hidden = true; + barcode_scanner(barcodeScanInputField); } } diff --git a/tally_ho/apps/tally/static/js/blank_field_check.js b/tally_ho/apps/tally/static/js/blank_field_check.js index 2c284742f..4ff5281a8 100644 --- a/tally_ho/apps/tally/static/js/blank_field_check.js +++ b/tally_ho/apps/tally/static/js/blank_field_check.js @@ -1,23 +1,19 @@ -function validate_results(blank_message) { - var required_fields = Array.filter( - document.getElementsByClassName("required"), - function(el) { - return el.nodeName === "INPUT"; +function validate_results(alertMessage) { + const requiredElements = document.querySelectorAll('.required-input'); + let isFormValid = true; + requiredElements.forEach(el => { + const inputChild = el.querySelector('input'); + if (inputChild && inputChild.value.trim() === '') { + isFormValid = false; + el.classList.add('has-error'); + } else if (inputChild) { + el.classList.remove('has-error'); } - ); + }); - var valid = true; - for (var i = 0; i < required_fields.length; i++) { - var elem = required_fields[i]; - if (elem.value === ""){ - valid = false; - elem.parentNode.setAttribute("class", "has-error"); - } else { - elem.parentNode.setAttribute("class", "has-success"); - } - } - if (valid === false) { - alert(blank_message); + if (isFormValid === false) { + alert(alertMessage); + return isFormValid; } - return valid; + return isFormValid; } diff --git a/tally_ho/apps/tally/static/js/download_candidates_list.js b/tally_ho/apps/tally/static/js/download_candidates_list.js index 913652ce6..7c44663a1 100644 --- a/tally_ho/apps/tally/static/js/download_candidates_list.js +++ b/tally_ho/apps/tally/static/js/download_candidates_list.js @@ -4,7 +4,7 @@ $(document).ready(function () { const a = document.createElement('a'); a.style.display = 'none'; a.href = url; - a.download = 'candidates-list.json'; + a.download = `candidates_list_${Date.now()}.json`; document.body.appendChild(a); a.click(); a.remove(); @@ -25,7 +25,7 @@ $(document).ready(function () { success: (data) => { downLoadCandidates(data); $("#export-candidates-list").removeAttr("disabled"); - $("#export-candidates-list").html("Export in JSON"); + $("#export-candidates-list").html("json"); }, }); }); diff --git a/tally_ho/apps/tally/static/js/download_centers_and_stations_list.js b/tally_ho/apps/tally/static/js/download_centers_and_stations_list.js index 769e26a1b..447d7464a 100644 --- a/tally_ho/apps/tally/static/js/download_centers_and_stations_list.js +++ b/tally_ho/apps/tally/static/js/download_centers_and_stations_list.js @@ -4,7 +4,7 @@ $(document).ready(function () { const a = document.createElement('a'); a.style.display = 'none'; a.href = url; - a.download = 'centers-and-stations-list.json'; + a.download = `centers_and_stations_list_${Date.now()}.json`; document.body.appendChild(a); a.click(); a.remove(); diff --git a/tally_ho/apps/tally/static/js/download_result_forms.js b/tally_ho/apps/tally/static/js/download_result_forms.js index 3012aaf6b..62439f5a0 100644 --- a/tally_ho/apps/tally/static/js/download_result_forms.js +++ b/tally_ho/apps/tally/static/js/download_result_forms.js @@ -10,24 +10,22 @@ $(document).ready(function () { a.remove(); }; - $("#result-forms-report").on("click", "#export-result-forms-presidential", function () { - $("#export-result-forms-presidential").html("Exporting..."); - $("#export-result-forms-presidential").prop("disabled", true); - const presidentialRaceTypeNumber = [5]; + $("#result-forms-report").on("click", "#export-result-forms", function () { + $("#export-result-forms").html("Exporting..."); + $("#export-result-forms").prop("disabled", true); $.ajax({ url: resultFormsDownloadUrl, data: { data: JSON.stringify({ tally_id: tallyId, - race_types: presidentialRaceTypeNumber }), }, traditional: true, dataType: 'json', success: (data) => { - downLoadResultForms(data, 'presidential_forms.json'); - $("#export-result-forms-presidential").removeAttr("disabled"); - $("#export-result-forms-presidential").html("Export Presidential Forms in JSON"); + downLoadResultForms(data, `result_forms_${Date.now()}.json`); + $("#export-result-forms").removeAttr("disabled"); + $("#export-result-forms").html("json"); }, }); }); diff --git a/tally_ho/apps/tally/static/js/download_results.js b/tally_ho/apps/tally/static/js/download_results.js index 1e47676aa..7dd4339c4 100644 --- a/tally_ho/apps/tally/static/js/download_results.js +++ b/tally_ho/apps/tally/static/js/download_results.js @@ -10,24 +10,42 @@ $(document).ready(function () { a.remove(); }; - $("#report").on("click", "#export-form-results-presidential", function () { - $("#export-form-results-presidential").html("Exporting..."); - $("#export-form-results-presidential").prop("disabled", true); - const presidentialRaceTypeNumber = [5]; + $("#in-report").on("click", "#export-results", function () { + $("#export-results").html("Exporting..."); + $("#export-results").prop("disabled", true); $.ajax({ url: resultsDownloadUrl, data: { data: JSON.stringify({ tally_id: tallyId, - race_types: presidentialRaceTypeNumber }), }, traditional: true, dataType: 'json', success: (data) => { - downloadResults(data, 'presidential_results.json'); - $("#export-form-results-presidential").removeAttr("disabled"); - $("#export-form-results-presidential").html("Export Presidential Results in JSON"); + downloadResults(data, `results_${Date.now()}.json`); + $("#export-results").removeAttr("disabled"); + $("#export-results").html("All Results JSON Export"); + }, + }); + }); + + $("#sub-cons-list-export").on("click", "#export-sub-cons", function () { + $("#export-sub-cons").html("Exporting..."); + $("#export-sub-cons").prop("disabled", true); + $.ajax({ + url: subConsDownloadUrl, + data: { + data: JSON.stringify({ + tally_id: tallyId, + }), + }, + traditional: true, + dataType: 'json', + success: (data) => { + downloadResults(data, `sub_cons_list_${Date.now()}.json`); + $("#export-sub-cons").removeAttr("disabled"); + $("#export-sub-cons").html("json"); }, }); }); diff --git a/tally_ho/apps/tally/templates/corrections/comparison.html b/tally_ho/apps/tally/templates/corrections/comparison.html index 45d6f1709..7d5b357d6 100644 --- a/tally_ho/apps/tally/templates/corrections/comparison.html +++ b/tally_ho/apps/tally/templates/corrections/comparison.html @@ -1,5 +1,5 @@ {% load i18n %} -

{% trans 'Results Form Corrections:' %} {% trans header %}

+

{% trans 'Results Form Corrections:' %} {% trans header %}

diff --git a/tally_ho/apps/tally/templates/corrections/required.html b/tally_ho/apps/tally/templates/corrections/required.html index 6d018ecc2..97f3a80bd 100644 --- a/tally_ho/apps/tally/templates/corrections/required.html +++ b/tally_ho/apps/tally/templates/corrections/required.html @@ -14,7 +14,7 @@

{{ errors }}

{% if form.errors %}

{{ form.errors }}

{% endif %} {% csrf_token %} {% if result_form.has_recon %} -

{% trans 'Reconciliation Form Corrections' %}

+

{% trans 'Reconciliation Form Corrections' %}: {% trans header %}

{% trans 'Corrections Required?' %}
diff --git a/tally_ho/apps/tally/templates/data/candidates.html b/tally_ho/apps/tally/templates/data/candidates.html index d17464628..48038eb7f 100644 --- a/tally_ho/apps/tally/templates/data/candidates.html +++ b/tally_ho/apps/tally/templates/data/candidates.html @@ -26,7 +26,7 @@

{{ report_title }}

{% endif %}
- +
{% trans 'Corrections Required?' %}
diff --git a/tally_ho/apps/tally/templates/data/forms.html b/tally_ho/apps/tally/templates/data/forms.html index 96c7a9208..72d6505d3 100644 --- a/tally_ho/apps/tally/templates/data/forms.html +++ b/tally_ho/apps/tally/templates/data/forms.html @@ -39,8 +39,7 @@

{{ header_text }}

{% if show_create_form_button %}
- - +
diff --git a/tally_ho/apps/tally/templates/data/sub_cons_list.html b/tally_ho/apps/tally/templates/data/sub_cons_list.html new file mode 100644 index 000000000..9effec3f0 --- /dev/null +++ b/tally_ho/apps/tally/templates/data/sub_cons_list.html @@ -0,0 +1,44 @@ +{% extends 'base.html' %} + +{% load i18n static %} + +{% block styles %} +{{ block.super }} + + + +{% endblock %} + +{% block javascript %} +{% include "data/table.html" with remote_url=remote_url sub_cons_list_download_url=sub_cons_list_download_url languageDE=languageDE %} +{% endblock %} + +{% block content %} + +

{{ report_title }}

+ +{% if messages %} + +{% endif %} + +
+ +
+ +
+ + + + + + + + + + +
{% trans 'Code' %}{% trans 'Name' %}{% trans 'Election Level' %}{% trans 'Sub Race' %}{% trans 'Ballot Number' %}
+{% endblock %} diff --git a/tally_ho/apps/tally/templates/data/table.html b/tally_ho/apps/tally/templates/data/table.html index 3eed64d33..1dacf9f57 100644 --- a/tally_ho/apps/tally/templates/data/table.html +++ b/tally_ho/apps/tally/templates/data/table.html @@ -28,6 +28,7 @@ const getCentersStationsUrl = '{{ get_centers_stations_url }}'; const getExportUrl = '{{ get_export_url }}'; const resultsDownloadUrl = '{{ results_download_url }}'; + const subConsDownloadUrl = '{{ sub_cons_list_download_url }}'; const resultFormsDownloadUrl = '{{ result_forms_download_url }}'; const centersAndStationsDownloadUrl = '{{ centers_and_stations_list_download_url }}'; const candidatesDownloadUrl = '{{ candidates_list_download_url }}'; diff --git a/tally_ho/apps/tally/templates/data_entry/enter_results_view.html b/tally_ho/apps/tally/templates/data_entry/enter_results_view.html index 128fb7e67..ffd799c02 100644 --- a/tally_ho/apps/tally/templates/data_entry/enter_results_view.html +++ b/tally_ho/apps/tally/templates/data_entry/enter_results_view.html @@ -10,151 +10,173 @@

{% trans 'Data Entry' %} {{ data_entry_number }}

{% include 'center_details.html' %} + {{ reconciliation_form.errors }} + {{ reconciliation_form.non_form_errors }} {% if result_form.has_recon %} +

{% trans 'Reconciliation section' %}

+
+
-

{% trans 'In all the fields below, please check the number of ballots twice to ensure accuracy' %}

{% trans 'Section (A) to be filled prior to opening the polling station' %}

- -

{% trans 'Reconciliation section' %}

-
- - - - - - - - - - -
- {% trans 'Serial number of ballots received by the polling station' %}
- + {% trans 'Total number of ballots papers received by the polling station' %} +
1
+
+
+

{% trans 'Section (B) reconciliation of the number of voter cards with ballot papers' %}

+
+
+ + + + + + + + +
9
+
+
+ + + + + + + + +
2
+
+

- {% trans 'section (d) to be filled after the completion of the results forms' %}

+ {% trans 'Section (E) to be filled in after completing the results forms' %}

- + - + - + - + {% trans 'Number of invalid votes (including empty ballot papers):' %} + - + - + - + - + {% trans 'Total number of the sorted and counted ballots (10 + 11 + 12):' %} + - + - + - + - + - +
910
1011
1112
1213

- {% 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.' %} + {% trans 'NB: If the number in field 2 is not equal to the number in field 9, the result should be recalculated in fields 5, 7.' %} +
+
+ {% trans 'If the difference remains after the recalculation, the cancelled ballot papers and ballot papers in the box shall be recounted in fields 5 and 7.' %} +
+
+ {% trans 'If the difference remains, write a note in the box below the form, stating the procedures followed before proceeding to the next step.' %} +
+
+ {% trans 'If the number in field 13 is not equal to the number in field 7, the result should be recalculated in fields 10, 11, and 12.' %}

- {% trans 'If after recounting, the difference persists, the ballots of the fields 9, 10 and 11 should be recounted as in the manual.' %} + {% trans 'If the difference remains after the recalculation, the ballot papers shall be recounted in fields 10, 11 , and 12 as indicated in the procedures manual.' %}

- {% 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.' %} + {% trans 'If the difference remains, write a note in the box below the form, stating the procedures followed before proceeding to the next step.' %}


- {% trans 'Section (B) to be filled after closure of polling station' %} + {% trans 'Section (C) to be filled in upon closing the station' %}

- - - - - - + - + - + - + {% trans 'Total number of ballots remaining outside the ballot box (3 + 4 + 5):' %} +
2
3
4
5
6

- {% trans 'Section (C) to be filled after counting the ballots in the box' %}

+ {% trans 'Section (D) to be completed after counting the ballot papers in the ballot box' %} +

- + - + {% trans 'Total number of ballots found inside and outside the ballot box (6 + 7):' %} +
7
8
@@ -169,6 +191,13 @@

{% trans 'Reconciliation section' %}

+ + + + + + +
{% endif %} @@ -186,16 +215,6 @@

{% trans 'Results Section' %}

{% trans 'Votes' %} {% for election_level, sub_race_type, form, candidate in forms_and_candidates %} - {% if election_level %} - -

{{ election_level }}

- - {% endif %} - {% if sub_race_type %} - -

{{ sub_race_type }}

- - {% endif %} {{ candidate.order }} diff --git a/tally_ho/apps/tally/templates/quality_control/reconciliation.html b/tally_ho/apps/tally/templates/quality_control/reconciliation.html index f48b8994e..995c5b20b 100644 --- a/tally_ho/apps/tally/templates/quality_control/reconciliation.html +++ b/tally_ho/apps/tally/templates/quality_control/reconciliation.html @@ -3,71 +3,105 @@ {% block content %}

{% trans 'Reconciliation Section' %}

- +
+
+
+
+

{% trans 'In all the fields below, please check the number of ballots twice to ensure accuracy' %}

+

{% trans 'Section (A) to be filled prior to opening the polling station' %}

+
+
- - - - - - - - - - -
- {% trans 'Serial number of ballots received by the polling station' %}{{ form.ballot_number_from.data }}{{ form.ballot_number_to.data }}
+ {% trans 'Total number of ballots papers received by the polling station' %}
1 {{ form.number_ballots_received.data }}
+
+
+

{% trans 'Section (B) reconciliation of the number of voter cards with ballot papers' %}

+
+
+ + + + + + + + +
9{{ form.total_of_cancelled_ballots_and_ballots_inside_box.data }}
+
+
+ + + + + + + + +
2{{ form.number_of_voter_cards_in_the_ballot_box.data }}
+
+

- {% trans 'Section (d) to be filled after the completion of the results forms' %}

+ {% trans 'Section (E) to be filled in after completing the results forms' %}

- + - + - + - + {% trans 'Number of invalid votes (including empty ballot papers):' %} + - + - + - + - + {% trans 'Total number of the sorted and counted ballots (10 + 11 + 12):' %} + @@ -85,59 +119,52 @@

{% trans 'Reconciliation Section' %}

-

- {% trans 'Section (B) to be filled after closure of polling station' %} -

+

+ {% trans 'Section (C) to be filled in upon closing the station' %} +

910 {{ form.number_unstamped_ballots.data }}{{ form.number_unstamped_ballots.data }}
1011 {{ form.number_invalid_votes.data }}{{ form.number_invalid_votes.data }}
1112 {{ form.number_valid_votes.data }}{{ form.number_valid_votes.data }}
1213 {{ form.number_sorted_and_counted.data }}{{ form.number_sorted_and_counted.data }}
- {% 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.' %} + {% trans 'NB: If the number in field 2 is not equal to the number in field 9, the result should be recalculated in fields 5, 7.' %} +
+
+ {% trans 'If the difference remains after the recalculation, the cancelled ballot papers and ballot papers in the box shall be recounted in fields 5 and 7.' %} +
+
+ {% trans 'If the difference remains, write a note in the box below the form, stating the procedures followed before proceeding to the next step.' %}

- {% trans 'If after recounting, the difference persists, the ballots of the fields 9, 10 and 11 should be recounted as in the manual.' %} + {% trans 'If the number in field 13 is not equal to the number in field 7, the result should be recalculated in fields 10, 11, and 12.' %}

- {% 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.' %} + {% trans 'If the difference remains after the recalculation, the ballot papers shall be recounted in fields 10, 11 , and 12 as indicated in the procedures manual.' %} +
+
+ {% trans 'If the difference remains, write a note in the box below the form, stating the procedures followed before proceeding to the next step.' %}
- - - - - - + - + - + - + {% trans 'Total number of ballots remaining outside the ballot box (3 + 4 + 5):' %} +
2{{ form.number_signatures_in_vr.data }}
3 {{ form.number_unused_ballots.data }}{{ form.number_unused_ballots.data }}
4 {{ form.number_spoiled_ballots.data }}{{ form.number_spoiled_ballots.data }}
5 {{ form.number_cancelled_ballots.data }}{{ form.number_cancelled_ballots.data }}
6 {{ form.number_ballots_outside_box.data }}{{ form.number_ballots_outside_box.data }}

- {% trans 'Section (C) to be filled after counting the ballots in the box' %}

+ {% trans 'Section (D) to be completed after counting the ballot papers in the ballot box' %}

- + - + {% trans 'Total number of ballots found inside and outside the ballot box (6 + 7):' %} + - - - - - - - -
7 {{ form.number_ballots_inside_box.data }}{{ form.number_ballots_inside_box.data }}
8 {{ form.number_ballots_inside_and_outside_box.data }}{{ form.number_ballots_inside_and_outside_box.data }}
diff --git a/tally_ho/apps/tally/templates/reports/form_results.html b/tally_ho/apps/tally/templates/reports/form_results.html index 3cdc6384c..e2674ba4e 100644 --- a/tally_ho/apps/tally/templates/reports/form_results.html +++ b/tally_ho/apps/tally/templates/reports/form_results.html @@ -265,6 +265,7 @@

{% trans 'Candidate Results' %}

+
@@ -379,4 +380,3 @@

{% trans 'Candidate Results' %}

{% endblock %} - diff --git a/tally_ho/apps/tally/templates/super_admin/home.html b/tally_ho/apps/tally/templates/super_admin/home.html index a0504952a..819aa7b71 100644 --- a/tally_ho/apps/tally/templates/super_admin/home.html +++ b/tally_ho/apps/tally/templates/super_admin/home.html @@ -15,6 +15,7 @@

{% trans 'Dashboard' %}

{% trans 'Data views' %}

{% trans 'Value:' %} - {{ form.value.errors }} - {{ form.value }} -
{% trans 'Description:' %} @@ -42,13 +35,6 @@

{% trans 'Quarantine checks List' %}

{{ form.description }}
{% trans 'Percentage:' %} - {{ form.percentage.errors }} - {{ form.percentage }} -
{% trans 'Active:' %} diff --git a/tally_ho/apps/tally/tests/forms/test_recon_form.py b/tally_ho/apps/tally/tests/forms/test_recon_form.py new file mode 100644 index 000000000..36e0a2bc3 --- /dev/null +++ b/tally_ho/apps/tally/tests/forms/test_recon_form.py @@ -0,0 +1,84 @@ +from tally_ho.libs.tests.test_base import TestBase, create_result_form +from tally_ho.libs.permissions import groups +from tally_ho.apps.tally.forms.recon_form import ReconForm +from django.utils.translation import gettext_lazy as _ + +class ReconFormTest(TestBase): + def setUp(self): + self._create_permission_groups() + self._create_and_login_user() + self._add_user_to_group(self.user, groups.TALLY_MANAGER) + self.result_form = create_result_form() + # Default valid data for the form + self.valid_data = { + 'result_form': self.result_form, + 'user': self.user, + 'is_stamped': True, + 'number_ballots_received': 100, + 'number_of_voter_cards_in_the_ballot_box': 100, + 'number_unused_ballots': 0, + 'number_spoiled_ballots': 0, + 'number_cancelled_ballots': 0, + 'number_ballots_outside_box': 0, + 'number_ballots_inside_box': 100, + 'number_ballots_inside_and_outside_box': 100, + 'total_of_cancelled_ballots_and_ballots_inside_box': 100, + # Should match 5 + 7 + 'number_unstamped_ballots': 0, + 'number_invalid_votes': 0, + 'number_valid_votes': 100, + 'number_sorted_and_counted': 100, + 'signature_polling_officer_1': True, + 'signature_polling_officer_2': True, + 'signature_polling_station_chair': True, + 'signature_dated': True, + } + + def test_valid_data(self): + """Test that the form is valid with correct data.""" + form = ReconForm(data=self.valid_data) + self.assertTrue(form.is_valid()) + + def test_missing_required_fields(self): + """Test that the form is invalid when required fields are missing.""" + invalid_data = self.valid_data.copy() + invalid_data.pop('number_valid_votes') # Remove a required field + form = ReconForm(data=invalid_data) + self.assertFalse(form.is_valid()) + self.assertIn('number_valid_votes', form.errors) + + def test_custom_clean_validation(self): + """Test that custom validation in clean method raises error with + incorrect totals.""" + invalid_data = self.valid_data.copy() + invalid_data['total_of_cancelled_ballots_and_ballots_inside_box'] =\ + 999 # Invalid total + form = ReconForm(data=invalid_data) + self.assertFalse(form.is_valid()) + + # Convert the error messages to strings for comparison + error_messages = [str(err) for err in form.errors['__all__']] + + # Check if the expected error message is in the error messages + self.assertIn( + str(_('Total of fied 5 and 7 is incorrect')), error_messages) + + def test_disable_copy_paste_attributes(self): + """Test that the copy/paste attributes are disabled on all + form fields.""" + form = ReconForm() + for field in form.fields.values(): + widget_attrs = field.widget.attrs + self.assertEqual(widget_attrs.get('onCopy'), 'return false;') + self.assertEqual(widget_attrs.get('onDrag'), 'return false;') + self.assertEqual(widget_attrs.get('onDrop'), 'return false;') + self.assertEqual(widget_attrs.get('onPaste'), 'return false;') + self.assertEqual(widget_attrs.get('autocomplete'), 'off') + self.assertIn('form-control', widget_attrs.get('class', '')) + + def test_required_field_class_added(self): + """Test that 'required' class is added to required fields.""" + form = ReconForm() + for field in form.fields.values(): + if field.required: + self.assertIn('required', field.widget.attrs.get('class', '')) diff --git a/tally_ho/apps/tally/tests/management/commands/test_async_import_result_form.py b/tally_ho/apps/tally/tests/management/commands/test_async_import_result_form.py index 4363dc0f4..411c726c3 100644 --- a/tally_ho/apps/tally/tests/management/commands/test_async_import_result_form.py +++ b/tally_ho/apps/tally/tests/management/commands/test_async_import_result_form.py @@ -114,3 +114,16 @@ def test_async_import_result_forms_with_duplicate_exception(self): tally_id=self.tally.id, csv_file_path=csv_file_path,) task.wait() + + def test_async_import_result_forms_with_invalid_centers_exception(self): + # Prepare test data with faulty file + csv_file_path =\ + str('tally_ho/libs/tests/fixtures/' + 'tally_setup_files/invalid_center_codes_result_forms.csv') + + # Call the task with faulty data that raises an exception + with self.assertRaises(Exception): + task = async_import_results_forms_from_result_forms_file.delay( + tally_id=self.tally.id, + csv_file_path=csv_file_path,) + task.wait() diff --git a/tally_ho/apps/tally/tests/views/data/test_sub_cons_list_view.py b/tally_ho/apps/tally/tests/views/data/test_sub_cons_list_view.py new file mode 100644 index 000000000..faf48f5e4 --- /dev/null +++ b/tally_ho/apps/tally/tests/views/data/test_sub_cons_list_view.py @@ -0,0 +1,55 @@ +import json +from django.test import RequestFactory + +from tally_ho.apps.tally.views.data import sub_constituency_list_view as views +from tally_ho.libs.permissions import groups +from tally_ho.libs.tests.test_base import TestBase +from tally_ho.libs.tests.fixtures.electrol_race_data import electrol_races +from tally_ho.libs.tests.test_base import ( + create_ballot, create_electrol_race, create_sub_constituency, create_tally +) + + +class TestSubConstituencyListView(TestBase): + def setUp(self): + self.factory = RequestFactory() + self._create_permission_groups() + self._create_and_login_user() + self._add_user_to_group(self.user, groups.SUPER_ADMINISTRATOR) + self.tally = create_tally() + self.tally.users.add(self.user) + electrol_race = create_electrol_race( + self.tally, + **electrol_races[0] + ) + ballot = create_ballot(self.tally, electrol_race=electrol_race) + create_sub_constituency( + code=1, + field_office='1', + ballots=[ballot], + name="Sub Con A", + tally=self.tally, + ) + + def test_sub_cons_list_view(self): + tally = create_tally() + tally.users.add(self.user) + view = views.SubConstituencyListView.as_view() + request = self.factory.get('/') + request.user = self.user + request.session = {} + response = view(request, tally_id=tally.pk) + self.assertContains(response, "Sub Constituencies List") + + def test_sub_cons_list_data_view(self): + """ + Test that sub cons list data view returns correct data + """ + view = views.SubConstituencyListDataView.as_view() + mock_json_data =\ + ['1', 'Sub Con A', 'Presidential', 'ballot_number_presidential', 1] + request = self.factory.get('/sub-cons-list-data') + request.user = self.user + response = view(request, tally_id=self.tally.pk) + self.assertEqual( + mock_json_data, json.loads(response.content.decode())['data'][0]) diff --git a/tally_ho/apps/tally/tests/views/test_corrections.py b/tally_ho/apps/tally/tests/views/test_corrections.py index a8433ec3b..674058b9b 100644 --- a/tally_ho/apps/tally/tests/views/test_corrections.py +++ b/tally_ho/apps/tally/tests/views/test_corrections.py @@ -561,6 +561,10 @@ def test_recon_post(self): tally=self.tally, electrol_race=self.electrol_race) + data_entry_1_user =\ + self._create_user('data_entry_1', 'password') + self._add_user_to_group(data_entry_1_user, + groups.DATA_ENTRY_1_CLERK) data_entry_2_user =\ self._create_user('data_entry_2', 'password') self._add_user_to_group(data_entry_2_user, @@ -573,7 +577,12 @@ def test_recon_post(self): form_processing_time_in_seconds =\ (end_time - start_time).total_seconds() - result_form_stat = create_result_form_stats( + result_form_stat_de_1 = create_result_form_stats( + processing_time=form_processing_time_in_seconds, + user=data_entry_1_user, + result_form=result_form + ) + result_form_stat_de_2 = create_result_form_stats( processing_time=form_processing_time_in_seconds, user=data_entry_2_user, result_form=result_form @@ -581,7 +590,6 @@ def test_recon_post(self): create_results(result_form, vote1=2, vote2=2) - ballot_from_val = '2' sorted_counted_val = 3 is_stamped = False @@ -592,7 +600,6 @@ def test_recon_post(self): recon2 = create_reconciliation_form( result_form, self.user, - ballot_number_from=ballot_from_val, number_sorted_and_counted=sorted_counted_val, is_stamped=is_stamped) recon2.entry_version = EntryVersion.DATA_ENTRY_2 @@ -605,7 +612,6 @@ def test_recon_post(self): session = {'result_form': result_form.pk} data = { 'submit_corrections': 1, - 'ballot_number_from': ballot_from_val, 'number_sorted_and_counted': sorted_counted_val, 'is_stamped': is_stamped, 'tally_id': self.tally.pk, @@ -618,7 +624,6 @@ def test_recon_post(self): final_form = ReconciliationForm.objects.filter( result_form=result_form, entry_version=EntryVersion.FINAL)[0] - self.assertEqual(final_form.ballot_number_from, ballot_from_val) self.assertEqual(final_form.is_stamped, is_stamped) self.assertEqual(final_form.number_sorted_and_counted, sorted_counted_val) @@ -633,8 +638,10 @@ def test_recon_post(self): FormState.QUALITY_CONTROL) self.assertIn('corrections/success', response['location']) - result_form_stat.reload() - self.assertEqual(result_form_stat.data_entry_errors, 1) + result_form_stat_de_1.reload() + result_form_stat_de_2.reload() + self.assertEqual(result_form_stat_de_1.data_entry_errors, 2) + self.assertEqual(result_form_stat_de_2.data_entry_errors, 0) def test_confirmation_get(self): result_form = create_result_form(form_state=FormState.QUALITY_CONTROL, diff --git a/tally_ho/apps/tally/tests/views/test_get_json_results.py b/tally_ho/apps/tally/tests/views/test_get_json_results.py new file mode 100644 index 000000000..3a05cd568 --- /dev/null +++ b/tally_ho/apps/tally/tests/views/test_get_json_results.py @@ -0,0 +1,86 @@ +from datetime import datetime +from django.urls import reverse +from tally_ho.libs.models.enums.gender import Gender +from tally_ho.libs.tests.test_base import TestBase +from django.http import JsonResponse +from unittest.mock import patch +import json +from tally_ho.libs.permissions import groups +from tally_ho.libs.tests.test_base import create_tally +from django.utils import timezone + +class GetJSONResultsTest(TestBase): + def setUp(self): + self._create_permission_groups() + self._create_and_login_user() + self._add_user_to_group(self.user, groups.TALLY_MANAGER) + self.tally = create_tally() + self.tally.users.add(self.user) + self.url = reverse('download-results') + + @patch('tally_ho.apps.tally.views.reports.administrative_areas_reports.' + 'results_queryset') + @patch('tally_ho.apps.tally.views.reports.administrative_areas_reports.' + 'total_valid_votes_with_recon_forms_per_electrol_race') + @patch('tally_ho.apps.tally.views.reports.administrative_areas_reports.' + 'total_valid_votes_with_no_recon_forms_per_electrol_race') + def test_get_results_success( + self, mock_no_recon, mock_with_recon, mock_results_queryset): + mock_results_queryset.return_value = [ + { + 'candidate_number': 1, + 'candidate_name': 'Candidate A', + 'total_votes': 100, + 'gender': Gender.MALE, + 'election_level': 'Level 1', + 'sub_race_type': 'Type A', + 'order': 1, + 'ballot_number': '1234', + 'candidate_status': 'active', + 'center_code': 'C1', + 'center_name': 'Center A', + 'office_number': 'O1', + 'office_name': 'Office A', + 'station_number': 'S1', + 'sub_con_code': 'SUB1', + 'electrol_race_id': 1, + 'sub_con_name': 'Sub Con A', + } + ] + mock_with_recon.return_value = 50 + mock_no_recon.return_value = 50 + + request_data = json.dumps({'tally_id': self.tally.id}) + response = self.client.get(self.url, {'data': request_data}) + self.assertIsInstance(response, JsonResponse) + + response_data = json.loads(response.content) + expected_data = { + 'data': [{ + 'candidate_id': 1, + 'candidate_name': 'Candidate A', + 'total_votes': 100, + 'gender': Gender.MALE.name, + 'election_level': 'Level 1', + 'sub_race_type': 'Type A', + 'order': 1, + 'ballot_number': '1234', + 'candidate_status': 'active', + 'center_code': 'C1', + 'center_name': 'Center A', + 'office_number': 'O1', + 'office_name': 'Office A', + 'station_number': 'S1', + 'sub_con_code': 'SUB1', + 'valid_votes': 100, + 'sub_con_name': 'Sub Con A', + }], + 'created_at': response_data['created_at'], + } + + self.assertEqual(response_data['data'], expected_data['data']) + + created_at = datetime.strptime( + response_data['created_at'], '%Y-%m-%dT%H:%M:%S.%fZ') + created_at = timezone.make_aware(created_at) + self.assertTrue(created_at <= timezone.now()) diff --git a/tally_ho/apps/tally/tests/views/test_get_json_sub_cons.py b/tally_ho/apps/tally/tests/views/test_get_json_sub_cons.py new file mode 100644 index 000000000..34dfe7e65 --- /dev/null +++ b/tally_ho/apps/tally/tests/views/test_get_json_sub_cons.py @@ -0,0 +1,58 @@ +from datetime import datetime +from django.urls import reverse +from tally_ho.libs.tests.test_base import TestBase +from tally_ho.libs.tests.test_base import ( + create_ballot, create_electrol_race, create_sub_constituency, create_tally +) +from django.http import JsonResponse +import json +from tally_ho.libs.permissions import groups +from django.utils import timezone +from tally_ho.libs.tests.fixtures.electrol_race_data import electrol_races + +class GetJSONSubConsTest(TestBase): + def setUp(self): + self._create_permission_groups() + self._create_and_login_user() + self._add_user_to_group(self.user, groups.TALLY_MANAGER) + self.tally = create_tally() + self.tally.users.add(self.user) + self.url = reverse('download-sub-cons-list') + electrol_race = create_electrol_race( + self.tally, + **electrol_races[0] + ) + ballot = create_ballot(self.tally, electrol_race=electrol_race) + create_sub_constituency( + code=1, + field_office='1', + ballots=[ballot], + name="Sub Con A", + tally=self.tally, + ) + + def test_get_sub_cons_success(self): + mock_queryset = [ + { + 'code': 1, + 'name': 'Sub Con A', + 'election_level': 'Presidential', + 'sub_race': 'ballot_number_presidential', + 'ballot_number': 1 + } + ] + request_data = json.dumps({'tally_id': self.tally.id}) + response = self.client.get(self.url, {'data': request_data}) + self.assertIsInstance(response, JsonResponse) + + response_data = json.loads(response.content) + expected_data = { + 'data': mock_queryset, + 'created_at': response_data['created_at'], + } + self.assertEqual(response_data['data'], expected_data['data']) + + created_at = datetime.strptime( + response_data['created_at'], '%Y-%m-%dT%H:%M:%S.%fZ') + created_at = timezone.make_aware(created_at) + self.assertTrue(created_at <= timezone.now()) diff --git a/tally_ho/apps/tally/tests/views/test_quality_control.py b/tally_ho/apps/tally/tests/views/test_quality_control.py index 1d40f6e84..9a1221bd7 100644 --- a/tally_ho/apps/tally/tests/views/test_quality_control.py +++ b/tally_ho/apps/tally/tests/views/test_quality_control.py @@ -1,4 +1,3 @@ -import copy from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.serializers.json import json, DjangoJSONEncoder from django.contrib.auth.models import AnonymousUser @@ -279,14 +278,12 @@ def test_dashboard_get_double_recon_raise(self): tally=self.tally, form_state=FormState.QUALITY_CONTROL) create_reconciliation_form(result_form, self.user) - create_reconciliation_form(result_form, self.user, - ballot_number_from=2) create_candidates(result_form, self.user) create_quality_control(result_form, self.user) self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) view = views.QualityControlDashboardView.as_view() - request = self.factory.get('/') + request = self.factory.post('/') request.user = self.user request.session = {'result_form': result_form.pk} @@ -712,164 +709,33 @@ def test_qc_view_post_abort(self): quality_control = result_form.qualitycontrol_set.all()[0] self.assertEqual(quality_control.active, False) - def test_quality_control_post_quarantine_pass_with_zero_diff(self): + def test_quality_control_post_passes_all_quarantine_triggers(self): """ - Test quality control post pass quarantine trigger with zero difference + Test quality control post passes all quarantine triggers """ - center = create_center() - create_station(center) create_quarantine_checks(self.quarantine_data) - result_form = create_result_form( - form_state=FormState.QUALITY_CONTROL, - center=center, - tally=self.tally, - station_number=1) - recon_form = create_reconciliation_form( - result_form, self.user, number_unstamped_ballots=0) - create_quality_control(result_form, self.user) - self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) - view = views.QualityControlDashboardView.as_view() - data = { - 'correct': 1, - 'result_form': result_form.pk, - 'tally_id': self.tally.pk, - } - request = self.factory.post('/', data=data) - request.session = {'result_form': result_form.pk} - request.user = self.user - response = view(request, tally_id=self.tally.pk) - result_form.reload() - - self.assertEqual(result_form.num_votes, - recon_form.number_ballots_expected) - - self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertIsNone(result_form.audit) - self.assertEqual(result_form.audited_count, 0) - self.assertIn('quality-control/print', response['location']) - - def test_quality_control_post_quarantine_pass_below_tolerance(self): - """ - Test quality control post pass quarantine trigger below tolerance - """ center = create_center() - create_station(center) - create_quarantine_checks(self.quarantine_data) + station = create_station(center=center, + registrants=50) result_form = create_result_form( - tally=self.tally, - form_state=FormState.QUALITY_CONTROL, - center=center, station_number=1) - recon_form = create_reconciliation_form( - result_form, self.user, number_ballots_inside_box=21, - number_unstamped_ballots=0) - create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) - self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) - view = views.QualityControlDashboardView.as_view() - data = { - 'correct': 1, - 'result_form': result_form.pk, - 'tally_id': self.tally.pk, - } - request = self.factory.post('/', data=data) - request.session = {'result_form': result_form.pk} - request.user = self.user - response = view(request, tally_id=self.tally.pk) - result_form.reload() - - self.assertEqual(result_form.num_votes, - recon_form.number_ballots_expected) - - self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertTrue(result_form.audit) - self.assertEqual(result_form.audit.quarantine_checks.count(), 1) - self.assertEqual( - result_form.audit.quarantine_checks.all()[0].name[:9], 'Trigger 1') - self.assertEqual(result_form.audited_count, 1) - self.assertIn('quality-control/print', response['location']) - - def test_quality_control_post_quarantine_pass_ballot_num_validation(self): - """ - Test that the total number of received ballots equals the - total of the ballots inside the box plus ballots outside the box - """ - center = create_center() - station = create_station(center=center, registrants=21) - quarantine_data = copy.deepcopy(self.quarantine_data) - quarantine_data[2]['active'] = True - create_quarantine_checks(quarantine_data) - result_form = create_result_form( - tally=self.tally, form_state=FormState.QUALITY_CONTROL, center=center, - station_number=station.station_number) - recon_form = create_reconciliation_form( - result_form=result_form, - user=self.user, - number_ballots_inside_box=21, - number_cancelled_ballots=0, - number_spoiled_ballots=0, - number_unstamped_ballots=0, - number_unused_ballots=0, - number_valid_votes=20, - number_ballots_received=21, - ) - create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) - self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) - view = views.QualityControlDashboardView.as_view() - data = { - 'correct': 1, - 'result_form': result_form.pk, - 'tally_id': self.tally.pk, - } - request = self.factory.post('/', data=data) - request.session = {'result_form': result_form.pk} - request.user = self.user - response = view(request, tally_id=self.tally.pk) - result_form.reload() - - self.assertEqual(result_form.num_votes, - recon_form.number_ballots_expected) - - self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertIsNone(result_form.audit) - self.assertEqual(result_form.audited_count, 0) - self.assertIn('quality-control/print', response['location']) - - def test_quality_control_post_quarantine_pass_signatures_validation(self): - """ - Test that the total number of signatures on the voter list equals - the number of ballots found in the ballot box after polling - plus cancelled ballots. - """ - center = create_center() - station = create_station(center=center, registrants=21) - quarantine_data = copy.deepcopy(self.quarantine_data) - quarantine_data[3]['active'] = True - create_quarantine_checks(quarantine_data) - result_form = create_result_form( tally=self.tally, - form_state=FormState.QUALITY_CONTROL, - center=center, station_number=station.station_number) - recon_form = create_reconciliation_form( - result_form=result_form, - user=self.user, - number_ballots_inside_box=21, - number_cancelled_ballots=0, - number_spoiled_ballots=0, - number_unstamped_ballots=0, - number_unused_ballots=0, - number_valid_votes=20, - number_ballots_received=21, - number_signatures_in_vr=21, - ) + recon_form =\ + create_reconciliation_form( + result_form=result_form, + user=self.user, + number_unstamped_ballots=0, + number_invalid_votes=0, + number_valid_votes=50, + number_ballots_inside_box=50, + number_of_voter_cards_in_the_ballot_box=50, + total_of_cancelled_ballots_and_ballots_inside_box=50, + number_sorted_and_counted=50, + ) create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) + create_candidates(result_form, self.user, votes=25, num_results=1) self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) view = views.QualityControlDashboardView.as_view() data = { @@ -883,95 +749,42 @@ def test_quality_control_post_quarantine_pass_signatures_validation(self): response = view(request, tally_id=self.tally.pk) result_form.reload() - self.assertEqual(result_form.num_votes, - recon_form.number_ballots_expected) - + self.assertEqual( + result_form.num_votes, + recon_form.total_of_cancelled_ballots_and_ballots_inside_box) self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) + self.assertNotEqual(result_form.form_state, FormState.AUDIT) self.assertIsNone(result_form.audit) self.assertEqual(result_form.audited_count, 0) self.assertIn('quality-control/print', response['location']) - def test_quality_control_quarantine_pass_ballots_inside_box_check(self): + def test_quality_control_post_fails_registrants_quarantine_trigger(self): """ - Test that the number of ballots value inside recon form matches the - total of valid, invalid, and unstamped ballots. + Test quality control post fails registrants quarantine trigger """ + create_quarantine_checks(self.quarantine_data) center = create_center() - station = create_station(center=center, registrants=21) - quarantine_data = copy.deepcopy(self.quarantine_data) - quarantine_data[4]['active'] = True - create_quarantine_checks(quarantine_data) + station = create_station( + center=center, + registrants=40) result_form = create_result_form( - tally=self.tally, form_state=FormState.QUALITY_CONTROL, center=center, - station_number=station.station_number) - recon_form = create_reconciliation_form( - result_form=result_form, - user=self.user, - number_ballots_inside_box=21, - number_cancelled_ballots=0, - number_spoiled_ballots=0, - number_unstamped_ballots=0, - number_unused_ballots=0, - number_valid_votes=20, - number_ballots_received=21, - number_signatures_in_vr=21, - ) - create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) - self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) - view = views.QualityControlDashboardView.as_view() - data = { - 'correct': 1, - 'result_form': result_form.pk, - 'tally_id': self.tally.pk, - } - request = self.factory.post('/', data=data) - request.session = {'result_form': result_form.pk} - request.user = self.user - response = view(request, tally_id=self.tally.pk) - result_form.reload() - - self.assertEqual(result_form.num_votes, - recon_form.number_ballots_expected) - - self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertIsNone(result_form.audit) - self.assertEqual(result_form.audited_count, 0) - self.assertIn('quality-control/print', response['location']) - - def test_quality_control_quarantine_pass_sum_candidates_votes_check(self): - """ - Test that the sum of candidates votes matches the total - number of valid ballots. - """ - center = create_center() - station = create_station(center=center, registrants=21) - quarantine_data = copy.deepcopy(self.quarantine_data) - quarantine_data[4]['active'] = True - create_quarantine_checks(quarantine_data) - result_form = create_result_form( tally=self.tally, - form_state=FormState.QUALITY_CONTROL, - center=center, station_number=station.station_number) - recon_form = create_reconciliation_form( + create_reconciliation_form( result_form=result_form, user=self.user, - number_ballots_inside_box=21, - number_cancelled_ballots=0, - number_spoiled_ballots=0, number_unstamped_ballots=0, - number_unused_ballots=0, - number_valid_votes=20, - number_ballots_received=21, - number_signatures_in_vr=21, + number_invalid_votes=0, + number_valid_votes=50, + number_ballots_inside_box=50, + number_of_voter_cards_in_the_ballot_box=50, + total_of_cancelled_ballots_and_ballots_inside_box=50, + number_sorted_and_counted=50, ) create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) + create_candidates(result_form, self.user, votes=25, num_results=1) self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) view = views.QualityControlDashboardView.as_view() data = { @@ -985,44 +798,41 @@ def test_quality_control_quarantine_pass_sum_candidates_votes_check(self): response = view(request, tally_id=self.tally.pk) result_form.reload() - self.assertEqual(result_form.num_votes, - recon_form.number_ballots_expected) - self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertIsNone(result_form.audit) - self.assertEqual(result_form.audited_count, 0) + self.assertIsNotNone(result_form.audit) + self.assertEqual(result_form.audited_count, 1) + self.assertEqual( + [c.method for c in result_form.audit.quarantine_checks.all()], + ['pass_registrants_trigger']) self.assertIn('quality-control/print', response['location']) - def test_quality_control_quarantine_pass_invalid_ballots_percentage(self): + def test_quality_control_post_fails_voter_cards_quarantine_trigger(self): """ - Test that the percentage of invalid ballots has not superseded the - allowed limit. + Test quality control post fails voter cards quarantine trigger """ + create_quarantine_checks(self.quarantine_data) center = create_center() - station = create_station(center=center, registrants=21) - quarantine_data = copy.deepcopy(self.quarantine_data) - quarantine_data[6]['active'] = True - create_quarantine_checks(quarantine_data) + station = create_station( + center=center, + registrants=50) result_form = create_result_form( - tally=self.tally, form_state=FormState.QUALITY_CONTROL, center=center, + tally=self.tally, station_number=station.station_number) create_reconciliation_form( result_form=result_form, user=self.user, - number_ballots_inside_box=21, - number_cancelled_ballots=0, - number_spoiled_ballots=0, number_unstamped_ballots=0, - number_unused_ballots=0, - number_valid_votes=20, - number_ballots_received=21, - number_signatures_in_vr=21, + number_invalid_votes=0, + number_valid_votes=50, + number_ballots_inside_box=50, + number_of_voter_cards_in_the_ballot_box=40, + total_of_cancelled_ballots_and_ballots_inside_box=50, + number_sorted_and_counted=50, ) create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) + create_candidates(result_form, self.user, votes=25, num_results=1) self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) view = views.QualityControlDashboardView.as_view() data = { @@ -1037,39 +847,42 @@ def test_quality_control_quarantine_pass_invalid_ballots_percentage(self): result_form.reload() self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertIsNone(result_form.audit) - self.assertEqual(result_form.audited_count, 0) + self.assertIsNotNone(result_form.audit) + self.assertEqual(result_form.audited_count, 1) + self.assertEqual( + [c.method for c in result_form.audit.quarantine_checks.all()], + ['pass_voter_cards_trigger']) self.assertIn('quality-control/print', response['location']) - def test_quality_control_quarantine_pass_turnout_percentage(self): + def test_quality_control_post_fails_ballot_papers_quarantine_trigger(self): """ - Test that the turnout percentage has not superseded the allowed limit. + Test quality control post fails ballot papers quarantine trigger """ + create_quarantine_checks(self.quarantine_data) center = create_center() - station = create_station(center=center, registrants=21) - quarantine_data = copy.deepcopy(self.quarantine_data) - quarantine_data[7]['active'] = True - create_quarantine_checks(quarantine_data) + station = create_station( + center=center, + registrants=50) result_form = create_result_form( - tally=self.tally, form_state=FormState.QUALITY_CONTROL, center=center, + tally=self.tally, station_number=station.station_number) create_reconciliation_form( result_form=result_form, user=self.user, - number_ballots_inside_box=21, - number_cancelled_ballots=0, - number_spoiled_ballots=0, number_unstamped_ballots=0, - number_unused_ballots=0, - number_valid_votes=20, - number_ballots_received=21, - number_signatures_in_vr=21, + number_invalid_votes=0, + number_valid_votes=50, + number_ballots_inside_box=50, + number_of_voter_cards_in_the_ballot_box=50, + total_of_cancelled_ballots_and_ballots_inside_box=50, + number_sorted_and_counted=50, + number_ballots_received=49, + number_ballots_inside_and_outside_box=50, ) create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) + create_candidates(result_form, self.user, votes=25, num_results=1) self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) view = views.QualityControlDashboardView.as_view() data = { @@ -1084,40 +897,43 @@ def test_quality_control_quarantine_pass_turnout_percentage(self): result_form.reload() self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertIsNone(result_form.audit) - self.assertEqual(result_form.audited_count, 0) + self.assertIsNotNone(result_form.audit) + self.assertEqual(result_form.audited_count, 1) + self.assertEqual( + [c.method for c in result_form.audit.quarantine_checks.all()], + ['pass_ballot_papers_trigger']) self.assertIn('quality-control/print', response['location']) - def test_quality_control_quarantine_pass_votes_candidate_percentage(self): + def test_quality_control_post_fails_ballot_inside_quarantine_trigger(self): """ - Test that the percentage of votes per candidate of the total - valid votes does not exceed a certain threshold. + Test quality control post fails ballot inside the box quarantine + trigger """ + create_quarantine_checks(self.quarantine_data) center = create_center() - station = create_station(center=center, registrants=21) - quarantine_data = copy.deepcopy(self.quarantine_data) - quarantine_data[8]['active'] = True - create_quarantine_checks(quarantine_data) + station = create_station( + center=center, + registrants=50) result_form = create_result_form( - tally=self.tally, form_state=FormState.QUALITY_CONTROL, center=center, + tally=self.tally, station_number=station.station_number) create_reconciliation_form( result_form=result_form, user=self.user, - number_ballots_inside_box=21, - number_cancelled_ballots=0, - number_spoiled_ballots=0, - number_unstamped_ballots=0, - number_unused_ballots=0, - number_valid_votes=20, - number_ballots_received=21, - number_signatures_in_vr=21, + number_unstamped_ballots=1, + number_invalid_votes=0, + number_valid_votes=50, + number_ballots_inside_box=50, + number_of_voter_cards_in_the_ballot_box=50, + total_of_cancelled_ballots_and_ballots_inside_box=50, + number_sorted_and_counted=50, + number_ballots_received=50, + number_ballots_inside_and_outside_box=50, ) create_quality_control(result_form, self.user) - create_candidates(result_form, self.user, votes=1, num_results=10) + create_candidates(result_form, self.user, votes=25, num_results=1) self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) view = views.QualityControlDashboardView.as_view() data = { @@ -1132,11 +948,444 @@ def test_quality_control_quarantine_pass_votes_candidate_percentage(self): result_form.reload() self.assertEqual(response.status_code, 302) - self.assertTrue(result_form.form_state, FormState.AUDIT) - self.assertIsNone(result_form.audit) - self.assertEqual(result_form.audited_count, 0) + self.assertIsNotNone(result_form.audit) + self.assertEqual(result_form.audited_count, 1) + self.assertEqual( + [c.method for c in result_form.audit.quarantine_checks.all()], + ['pass_ballot_inside_box_trigger']) self.assertIn('quality-control/print', response['location']) + # Disabled old quarantine triggers test: + # Awaiting client feedback for final removal. + # def test_quality_control_post_quarantine_pass_with_zero_diff(self): + # """ + # Test quality control post pass quarantine trigger with zero + # difference + # """ + # center = create_center() + # create_station(center) + # create_quarantine_checks(self.quarantine_data) + # result_form = create_result_form( + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # tally=self.tally, + # station_number=1) + # recon_form = create_reconciliation_form( + # result_form, self.user, number_unstamped_ballots=0) + # create_quality_control(result_form, self.user) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(result_form.num_votes, + # recon_form.number_ballots_expected) + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + + # def test_quality_control_post_quarantine_pass_below_tolerance(self): + # """ + # Test quality control post pass quarantine trigger below tolerance + # """ + # center = create_center() + # create_station(center) + # create_quarantine_checks(self.quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, station_number=1) + # recon_form = create_reconciliation_form( + # result_form, self.user, number_ballots_inside_box=21, + # number_unstamped_ballots=0) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(result_form.num_votes, + # recon_form.number_ballots_expected) + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertTrue(result_form.audit) + # self.assertEqual(result_form.audit.quarantine_checks.count(), 1) + # self.assertEqual( + # result_form.audit.quarantine_checks.all()[0].name[:9], + # 'Trigger 1') + # self.assertEqual(result_form.audited_count, 1) + # self.assertIn('quality-control/print', response['location']) + + # def test_qc_post_quarantine_pass_ballot_num_validation(self): + # """ + # Test that the total number of received ballots equals the + # total of the ballots inside the box plus ballots outside the box + # """ + # center = create_center() + # station = create_station(center=center, registrants=21) + # quarantine_data = copy.deepcopy(self.quarantine_data) + # quarantine_data[2]['active'] = True + # create_quarantine_checks(quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # station_number=station.station_number) + # recon_form = create_reconciliation_form( + # result_form=result_form, + # user=self.user, + # number_ballots_inside_box=21, + # number_cancelled_ballots=0, + # number_spoiled_ballots=0, + # number_unstamped_ballots=0, + # number_unused_ballots=0, + # number_valid_votes=20, + # number_ballots_received=21, + # ) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(result_form.num_votes, + # recon_form.number_ballots_expected) + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + + # def test_qc_post_quarantine_pass_signatures_validation(self): + # """ + # Test that the total number of signatures on the voter list equals + # the number of ballots found in the ballot box after polling + # plus cancelled ballots. + # """ + # center = create_center() + # station = create_station(center=center, registrants=21) + # quarantine_data = copy.deepcopy(self.quarantine_data) + # quarantine_data[3]['active'] = True + # create_quarantine_checks(quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # station_number=station.station_number) + # recon_form = create_reconciliation_form( + # result_form=result_form, + # user=self.user, + # number_ballots_inside_box=21, + # number_cancelled_ballots=0, + # number_spoiled_ballots=0, + # number_unstamped_ballots=0, + # number_unused_ballots=0, + # number_valid_votes=20, + # number_ballots_received=21, + # number_signaturnumber_of_voter_cards_in_the_ballot_boxes_in_vr=\ + # 21, + # ) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(result_form.num_votes, + # recon_form.number_ballots_expected) + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + + # def test_quality_control_quarantine_pass_ballots_inside_box_check(self): + # """ + # Test that the number of ballots value inside recon form matches the + # total of valid, invalid, and unstamped ballots. + # """ + # center = create_center() + # station = create_station(center=center, registrants=21) + # quarantine_data = copy.deepcopy(self.quarantine_data) + # quarantine_data[4]['active'] = True + # create_quarantine_checks(quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # station_number=station.station_number) + # recon_form = create_reconciliation_form( + # result_form=result_form, + # user=self.user, + # number_ballots_inside_box=21, + # number_cancelled_ballots=0, + # number_spoiled_ballots=0, + # number_unstamped_ballots=0, + # number_unused_ballots=0, + # number_valid_votes=20, + # number_ballots_received=21, + # number_of_voter_cards_in_the_ballot_box=21, + # ) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(result_form.num_votes, + # recon_form.number_ballots_expected) + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + + # def test_qc_quarantine_pass_sum_candidates_votes_check(self): + # """ + # Test that the sum of candidates votes matches the total + # number of valid ballots. + # """ + # center = create_center() + # station = create_station(center=center, registrants=21) + # quarantine_data = copy.deepcopy(self.quarantine_data) + # quarantine_data[4]['active'] = True + # create_quarantine_checks(quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # station_number=station.station_number) + # recon_form = create_reconciliation_form( + # result_form=result_form, + # user=self.user, + # number_ballots_inside_box=21, + # number_cancelled_ballots=0, + # number_spoiled_ballots=0, + # number_unstamped_ballots=0, + # number_unused_ballots=0, + # number_valid_votes=20, + # number_ballots_received=21, + # number_of_voter_cards_in_the_ballot_box=21, + # ) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(result_form.num_votes, + # recon_form.number_ballots_expected) + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + + # def test_qc_quarantine_pass_invalid_ballots_percentage(self): + # """ + # Test that the percentage of invalid ballots has not superseded the + # allowed limit. + # """ + # center = create_center() + # station = create_station(center=center, registrants=21) + # quarantine_data = copy.deepcopy(self.quarantine_data) + # quarantine_data[6]['active'] = True + # create_quarantine_checks(quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # station_number=station.station_number) + # create_reconciliation_form( + # result_form=result_form, + # user=self.user, + # number_ballots_inside_box=21, + # number_cancelled_ballots=0, + # number_spoiled_ballots=0, + # number_unstamped_ballots=0, + # number_unused_ballots=0, + # number_valid_votes=20, + # number_ballots_received=21, + # number_of_voter_cards_in_the_ballot_box=21, + # ) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + + # def test_quality_control_quarantine_pass_turnout_percentage(self): + # """ + # Test that the turnout percentage has not superseded the allowed + # limit. + # """ + # center = create_center() + # station = create_station(center=center, registrants=21) + # quarantine_data = copy.deepcopy(self.quarantine_data) + # quarantine_data[7]['active'] = True + # create_quarantine_checks(quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # station_number=station.station_number) + # create_reconciliation_form( + # result_form=result_form, + # user=self.user, + # number_ballots_inside_box=21, + # number_cancelled_ballots=0, + # number_spoiled_ballots=0, + # number_unstamped_ballots=0, + # number_unused_ballots=0, + # number_valid_votes=20, + # number_ballots_received=21, + # number_of_voter_cards_in_the_ballot_box=21, + # ) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + + # def test_qc_quarantine_pass_votes_candidate_percentage(self): + # """ + # Test that the percentage of votes per candidate of the total + # valid votes does not exceed a certain threshold. + # """ + # center = create_center() + # station = create_station(center=center, registrants=21) + # quarantine_data = copy.deepcopy(self.quarantine_data) + # quarantine_data[8]['active'] = True + # create_quarantine_checks(quarantine_data) + # result_form = create_result_form( + # tally=self.tally, + # form_state=FormState.QUALITY_CONTROL, + # center=center, + # station_number=station.station_number) + # create_reconciliation_form( + # result_form=result_form, + # user=self.user, + # number_ballots_inside_box=21, + # number_cancelled_ballots=0, + # number_spoiled_ballots=0, + # number_unstamped_ballots=0, + # number_unused_ballots=0, + # number_valid_votes=20, + # number_ballots_received=21, + # number_of_voter_cards_in_the_ballot_box=21, + # ) + # create_quality_control(result_form, self.user) + # create_candidates(result_form, self.user, votes=1, num_results=10) + # self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) + # view = views.QualityControlDashboardView.as_view() + # data = { + # 'correct': 1, + # 'result_form': result_form.pk, + # 'tally_id': self.tally.pk, + # } + # request = self.factory.post('/', data=data) + # request.session = {'result_form': result_form.pk} + # request.user = self.user + # response = view(request, tally_id=self.tally.pk) + # result_form.reload() + + # self.assertEqual(response.status_code, 302) + # self.assertTrue(result_form.form_state, FormState.AUDIT) + # self.assertIsNone(result_form.audit) + # self.assertEqual(result_form.audited_count, 0) + # self.assertIn('quality-control/print', response['location']) + def test_quality_control_post_quarantine(self): """ Test quality control post @@ -1150,7 +1399,7 @@ def test_quality_control_post_quarantine(self): center=center, station_number=1) create_reconciliation_form( - result_form, self.user, number_unstamped_ballots=1000) + result_form, self.user, number_valid_votes=2) create_quality_control(result_form, self.user) self._add_user_to_group(self.user, groups.QUALITY_CONTROL_CLERK) view = views.QualityControlDashboardView.as_view() @@ -1168,7 +1417,13 @@ def test_quality_control_post_quarantine(self): self.assertEqual(response.status_code, 302) self.assertTrue(result_form.form_state, FormState.AUDIT) self.assertTrue(result_form.audit) - self.assertEqual(result_form.audit.quarantine_checks.count(), 2) + self.assertEqual( + result_form.audit.quarantine_checks.count(), + 2) + self.assertEqual( + [c.method for c in result_form.audit.quarantine_checks.all()], + ['pass_ballot_inside_box_trigger', + 'pass_candidates_votes_trigger']) self.assertEqual(result_form.audit.user, self.user) self.assertEqual(result_form.audited_count, 1) self.assertIn('quality-control/print', response['location']) diff --git a/tally_ho/apps/tally/tests/views/test_super_admin.py b/tally_ho/apps/tally/tests/views/test_super_admin.py index e95401d65..d0fd9fa16 100644 --- a/tally_ho/apps/tally/tests/views/test_super_admin.py +++ b/tally_ho/apps/tally/tests/views/test_super_admin.py @@ -1751,7 +1751,7 @@ def test_quarantine_checks_config_list_view(self): quarantine_data = getattr(settings, 'QUARANTINE_DATA') create_quarantine_checks(quarantine_data) quarantine_check = QuarantineCheck.objects.get( - name='Trigger 1 - Guard against overvoting') + method='pass_registrants_trigger') view = views.QuarantineChecksConfigView.as_view() request = self.factory.get('/') request.user = self.user diff --git a/tally_ho/apps/tally/views/data/candidate_list_view.py b/tally_ho/apps/tally/views/data/candidate_list_view.py index 68213db6a..afb950d64 100644 --- a/tally_ho/apps/tally/views/data/candidate_list_view.py +++ b/tally_ho/apps/tally/views/data/candidate_list_view.py @@ -57,7 +57,8 @@ def filter_queryset(self, qs): qs = qs.filter(tally__id=tally_id) if keyword: - qs = qs.filter(Q(full_name__contains=keyword)) + qs = qs.filter(Q(full_name__contains=keyword)| + Q(ballot__electrol_race__ballot_name=keyword)) return qs diff --git a/tally_ho/apps/tally/views/data/center_list_view.py b/tally_ho/apps/tally/views/data/center_list_view.py index 9ddec8875..409b2e54a 100644 --- a/tally_ho/apps/tally/views/data/center_list_view.py +++ b/tally_ho/apps/tally/views/data/center_list_view.py @@ -169,6 +169,7 @@ def get_centers_stations_list(request): office_id=F('center__office__id'), office_name=F('center__office__name'), office_number=F('center__office__number'), + sub_constituency_name=F('sub_constituency__name'), sub_constituency_code=F('sub_constituency__code'), region_id=F('center__office__region__id'), region_name=F('center__office__region__name'), @@ -178,6 +179,8 @@ def get_centers_stations_list(request): 'region_name', 'office_id', 'office_name', + 'office_number', + 'sub_constituency_name', 'sub_constituency_code', 'center_code', 'center_name', diff --git a/tally_ho/apps/tally/views/data/form_list_view.py b/tally_ho/apps/tally/views/data/form_list_view.py index dfa7d22f7..55af70c53 100644 --- a/tally_ho/apps/tally/views/data/form_list_view.py +++ b/tally_ho/apps/tally/views/data/form_list_view.py @@ -298,7 +298,6 @@ def get_result_forms(request): returns: A JSON response of result forms """ tally_id = json.loads(request.GET.get('data')).get('tally_id') - race_types = json.loads(request.GET.get('data')).get('race_types') station_id_query = \ Subquery( Station.objects.filter( @@ -311,14 +310,19 @@ def get_result_forms(request): output_field=IntegerField()) form_list = ResultForm.objects.filter( - tally__id=tally_id, - ballot__electrol_race__election_level__in=race_types)\ + tally__id=tally_id)\ .annotate( center_code=F('center__code'), + center_name=F('center__name'), office_name=F('center__office__name'), office_number=F('center__office__number'), region_id=F('center__office__region__id'), region_name=F('center__office__region__name'), + election_level=F('ballot__electrol_race__election_level'), + sub_race=F('ballot__electrol_race__ballot_name'), + ballot_number=F('ballot__number'), + sub_con_name=F('center__sub_constituency__name'), + sub_con_code=F('center__sub_constituency__code'), station_id=station_id_query) \ .values( 'id', @@ -326,6 +330,7 @@ def get_result_forms(request): 'barcode', 'station_id', 'station_number', + 'center_name', 'center_code', 'office_id', 'office_name', @@ -333,6 +338,11 @@ def get_result_forms(request): 'region_id', 'region_name', 'form_state', + 'election_level', + 'sub_race', + 'ballot_number', + 'sub_con_name', + 'sub_con_code', ) for form in form_list: diff --git a/tally_ho/apps/tally/views/data/sub_constituency_list_view.py b/tally_ho/apps/tally/views/data/sub_constituency_list_view.py new file mode 100644 index 000000000..e502bdc3e --- /dev/null +++ b/tally_ho/apps/tally/views/data/sub_constituency_list_view.py @@ -0,0 +1,98 @@ +from django.db.models import Q, F +from django.views.generic import TemplateView +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ +from django_datatables_view.base_datatable_view import BaseDatatableView +from guardian.mixins import LoginRequiredMixin + +from tally_ho.apps.tally.models.sub_constituency import SubConstituency +from tally_ho.libs.permissions import groups +from tally_ho.libs.utils.context_processors import ( + get_datatables_language_de_from_locale +) +from tally_ho.libs.views import mixins + + +class SubConstituencyListDataView(LoginRequiredMixin, + mixins.GroupRequiredMixin, + mixins.TallyAccessMixin, + BaseDatatableView): + group_required = groups.SUPER_ADMINISTRATOR + model = SubConstituency + columns = ( + 'code', + 'name', + 'election_level', + 'sub_race', + 'ballot_number', + ) + + def render_column(self, row, column): + if column == 'election_level': + return row['election_level'] + if column == 'sub_race': + return row['sub_race'] + elif column == 'ballot_number': + return row['ballot_number'] + else: + return super(SubConstituencyListDataView, self).render_column( + row, column) + + + def filter_queryset(self, qs): + qs = self.get_initial_queryset() + tally_id = self.kwargs.get('tally_id') + keyword = self.request.POST.get('search[value]') + + if tally_id: + qs = qs.filter(tally__id=tally_id) + else: + qs = qs.filter(tally__id=tally_id) + + if keyword: + qs = qs.filter(Q(name__contains=keyword)| + Q(election_level__contains=keyword)) + + return qs + + def get_initial_queryset(self): + """Get the initial queryset of SubConstituencies, but flatten the + results to include one row per SubConstituency per ElectrolRace. + """ + tally_id = self.kwargs.get('tally_id') + return SubConstituency.objects.filter( + tally__id=tally_id).prefetch_related('ballots__electrol_race')\ + .values( + 'code', + 'name', + 'ballots__electrol_race__election_level', + 'ballots__electrol_race__ballot_name', + 'ballots__number', + ).annotate( + election_level=F('ballots__electrol_race__election_level'), + sub_race=F('ballots__electrol_race__ballot_name'), + ballot_number=F('ballots__number') + ) + + +class SubConstituencyListView(LoginRequiredMixin, + mixins.GroupRequiredMixin, + mixins.TallyAccessMixin, + TemplateView): + group_required = groups.SUPER_ADMINISTRATOR + template_name = "data/sub_cons_list.html" + + def get(self, *args, **kwargs): + tally_id = self.kwargs.get('tally_id') + reverse_url = 'sub-cons-list-data' + report_title = _('Sub Constituencies List') + language_de = get_datatables_language_de_from_locale(self.request) + + return self.render_to_response(self.get_context_data( + remote_url=reverse( + reverse_url, + kwargs=kwargs), + tally_id=tally_id, + report_title=report_title, + sub_cons_list_download_url='/ajax/download-sub-cons-list/', + languageDE=language_de)) diff --git a/tally_ho/apps/tally/views/reports/administrative_areas_reports.py b/tally_ho/apps/tally/views/reports/administrative_areas_reports.py index e456efa95..b893abb8e 100644 --- a/tally_ho/apps/tally/views/reports/administrative_areas_reports.py +++ b/tally_ho/apps/tally/views/reports/administrative_areas_reports.py @@ -748,9 +748,15 @@ def results_queryset( qs = qs.filter(candidate__full_name__isnull=False)\ .values('candidate_id')\ .annotate( + candidate_number=F('candidate__candidate_id'), candidate_name=F('candidate__full_name'), total_votes=Sum('votes'), gender=F('result_form__gender'), + center_code=F('result_form__center__code'), + center_name=F('result_form__center__name'), + office_number=F('result_form__office__number'), + office_name=F('result_form__office__name'), + station_number=F('result_form__station_number'), electrol_race_id=F( 'result_form__ballot__electrol_race__id'), election_level=F( @@ -759,6 +765,8 @@ def results_queryset( 'result_form__ballot__electrol_race__ballot_name'), sub_con_name=F( 'result_form__center__sub_constituency__name'), + sub_con_code=F( + 'result_form__center__sub_constituency__code'), order=F('candidate__order'), ballot_number=F('candidate__ballot__number'), candidate_status=Case( @@ -772,9 +780,15 @@ def results_queryset( qs = qs.filter(candidate__full_name__isnull=False)\ .values('candidate_id')\ .annotate( + candidate_number=F('candidate__candidate_id'), candidate_name=F('candidate__full_name'), total_votes=Sum('votes'), gender=F('result_form__gender'), + center_code=F('result_form__center__code'), + center_name=F('result_form__center__name'), + office_number=F('result_form__office__number'), + office_name=F('result_form__office__name'), + station_number=F('result_form__station_number'), electrol_race_id=F( 'result_form__ballot__electrol_race__id'), election_level=F( @@ -783,6 +797,8 @@ def results_queryset( 'result_form__ballot__electrol_race__ballot_name'), sub_con_name=F( 'result_form__center__sub_constituency__name'), + sub_con_code=F( + 'result_form__center__sub_constituency__code'), order=F('candidate__order'), ballot_number=F('candidate__ballot__number'), candidate_status=Case( @@ -2108,8 +2124,9 @@ def create_results_power_point_headers(tally_id, filtered_electrol_races, qs): active=True, ).annotate( race=F( - 'result_form__ballot__electrol_race__election_level') - ).values('race').annotate( + 'result_form__ballot__electrol_race__election_level'), + ballot_number=F('result_form__ballot__number') + ).values('race', 'ballot_number').annotate( race_voters=Sum('votes') ).order_by( '-race_voters' @@ -2147,29 +2164,95 @@ def get_results(request): returns: A JSON response of candidates results """ tally_id = json.loads(request.GET.get('data')).get('tally_id') - race_types = json.loads(request.GET.get('data')).get('race_types') - qs = Result.objects.filter( result_form__tally__id=tally_id, result_form__form_state=FormState.ARCHIVED, - result_form__ballot__electrol_race__election_level__in=\ - race_types, entry_version=EntryVersion.FINAL, + result_form__ballot__available_for_release=True, active=True) + qs = results_queryset( + tally_id, + qs, + data=None + ) + electrol_race_ids =\ + list(set([result.get('electrol_race_id') for result in qs])) + valid_votes_per_electrol_race_id =\ + { + id: total_valid_votes_with_recon_forms_per_electrol_race( + tally_id, + id + ) + + total_valid_votes_with_no_recon_forms_per_electrol_race( + tally_id, + id + ) for id in electrol_race_ids + } + results =\ + [{ + 'candidate_id': result.get('candidate_number'), + 'candidate_name': result.get('candidate_name'), + 'total_votes': result.get('total_votes'), + 'gender': result.get('gender').name, + 'election_level': result.get('election_level'), + 'sub_race_type': result.get('sub_race_type'), + 'order': result.get('order'), + 'ballot_number': result.get('ballot_number'), + 'candidate_status': result.get('candidate_status'), + 'center_code': result.get('center_code'), + 'center_name': result.get('center_name'), + 'office_number': result.get('office_number'), + 'office_name': result.get('office_name'), + 'station_number': result.get('station_number'), + 'sub_con_code': result.get('sub_con_code'), + 'valid_votes': + valid_votes_per_electrol_race_id.get( + result.get('electrol_race_id')), + 'sub_con_name': result.get('sub_con_name'), + } for result in qs] + + sorted_results_report = \ + sorted(results, key=lambda x: -x['total_votes']) + + return JsonResponse( + data={'data': sorted_results_report, 'created_at': timezone.now()}, + safe=False) + + +def get_sub_cons_list(request): + """ + Builds a json object of sub constituencies. + + :param request: The request object containing the tally id. - data = results_queryset(tally_id, qs).values( - 'candidate_name', - 'total_votes', - 'ballot_number', - 'order', - 'candidate_status', - 'valid_votes', - 'election_level', - 'sub_race_type', + returns: A JSON response of sub constituencies results + """ + tally_id = json.loads(request.GET.get('data')).get('tally_id') + qs = SubConstituency.objects.filter( + tally__id=tally_id).prefetch_related('ballots__electrol_race')\ + .values( + 'code', + 'name', + 'field_office', + 'ballots__electrol_race__election_level', + 'ballots__electrol_race__ballot_name', + 'ballots__number', + ).annotate( + election_level=F('ballots__electrol_race__election_level'), + sub_race=F('ballots__electrol_race__ballot_name'), + ballot_number=F('ballots__number') ) + subs =\ + [{ + 'code': sub.get('code'), + 'name': sub.get('name'), + 'election_level': sub.get('election_level'), + 'sub_race': sub.get('sub_race'), + 'ballot_number': sub.get('ballot_number'), + } for sub in qs] return JsonResponse( - data={'data': list(data), 'created_at': timezone.now()}, + data={'data': subs, 'created_at': timezone.now()}, safe=False) class SummaryReportDataView(LoginRequiredMixin, diff --git a/tally_ho/apps/tally/views/reports/election_statistics_report.py b/tally_ho/apps/tally/views/reports/election_statistics_report.py index e98393a87..66cb6c37b 100644 --- a/tally_ho/apps/tally/views/reports/election_statistics_report.py +++ b/tally_ho/apps/tally/views/reports/election_statistics_report.py @@ -320,13 +320,13 @@ def generate_overview_election_statistics(tally_id, election_level): stations_counted += 1 total_registrants_in_counted_stations +=\ station.get('num_registrants') - if station.get('gender') == Gender.MALE: + if station.get('station_gender') == Gender.MALE: total_male_registrants_in_counted_stations +=\ station.get('num_registrants') - elif station.get('gender') == Gender.FEMALE: + elif station.get('station_gender') == Gender.FEMALE: total_female_registrants_in_counted_stations +=\ station.get('num_registrants') - elif station.get('gender') == Gender.UNISEX: + elif station.get('station_gender') == Gender.UNISEX: total_unisex_registrants_in_counted_stations +=\ station.get('num_registrants') @@ -342,8 +342,10 @@ def generate_overview_election_statistics(tally_id, election_level): active=True, ).annotate( race=F( - 'result_form__ballot__electrol_race__election_level') - ).values('race').annotate( + 'result_form__ballot__electrol_race__election_level'), + ballot_number=F( + 'result_form__ballot__number') + ).values('race', 'ballot_number').annotate( race_voters=Sum('votes') ).order_by( '-race_voters' @@ -352,11 +354,11 @@ def generate_overview_election_statistics(tally_id, election_level): ) if votes.count() != 0: voters += votes[0].get('race_voters') - if station.get('gender') == Gender.MALE: + if station.get('station_gender') == Gender.MALE: male_voters += votes[0].get('race_voters') - elif station.get('gender') == Gender.FEMALE: + elif station.get('station_gender') == Gender.FEMALE: female_voters += votes[0].get('race_voters') - elif station.get('gender') == Gender.UNISEX: + elif station.get('station_gender') == Gender.UNISEX: unisex_voters += votes[0].get('race_voters') # Calculate turnout percentage diff --git a/tally_ho/apps/tally/views/reports/turnout_reports_by_admin_areas.py b/tally_ho/apps/tally/views/reports/turnout_reports_by_admin_areas.py index 4f8ea1fbd..f72c2df0a 100644 --- a/tally_ho/apps/tally/views/reports/turnout_reports_by_admin_areas.py +++ b/tally_ho/apps/tally/views/reports/turnout_reports_by_admin_areas.py @@ -116,8 +116,9 @@ def get_station_votes_in_admin_area( entry_version=EntryVersion.FINAL, active=True, ).annotate( - race=F('result_form__ballot__electrol_race_id') - ).values('race').annotate( + race=F('result_form__ballot__electrol_race_id'), + ballot_number=F('result_form__ballot__number') + ).values('race', 'ballot_number').annotate( race_voters=Sum('votes') ).order_by( '-race_voters' diff --git a/tally_ho/libs/tests/fixtures/tally_setup_files/centers.csv b/tally_ho/libs/tests/fixtures/tally_setup_files/centers.csv index 898248ac8..a9bbfae9f 100644 --- a/tally_ho/libs/tests/fixtures/tally_setup_files/centers.csv +++ b/tally_ho/libs/tests/fixtures/tally_setup_files/centers.csv @@ -1,2 +1,3 @@ center_id,name,center_type,center_lat,center_lon,region_name,office_name,office_id,constituency_name,subconstituency_id,mahalla_name,village_name,reg_open -31001,مدرسة شهداء عين الغزالة للتعليم الأساسي,General,32.144404,23.356148,East,Tubruq,12,الأولى,1,عين الغزالة,طبرق,1 \ No newline at end of file +31001,مدرسة شهداء عين الغزالة للتعليم الأساسي,General,32.144404,23.356148,East,Tubruq,12,الأولى,1,عين الغزالة,طبرق,1 +31002,Test Center,General,32.144404,23.356148,East,Tubruq,12,Test,1,عين الغزالة,طبرق,1 diff --git a/tally_ho/libs/tests/fixtures/tally_setup_files/invalid_center_codes_result_forms.csv b/tally_ho/libs/tests/fixtures/tally_setup_files/invalid_center_codes_result_forms.csv new file mode 100644 index 000000000..fde2e7ec7 --- /dev/null +++ b/tally_ho/libs/tests/fixtures/tally_setup_files/invalid_center_codes_result_forms.csv @@ -0,0 +1,2 @@ +ballot_number,center_code,station_number,gender,name,office_name,barcode,serial_number,region_name +1,310012,2,female,مدرسة شهداء عين الغزالة للتعليم الأساسي,Tubruq,31001002001,,East \ No newline at end of file diff --git a/tally_ho/libs/tests/test_base.py b/tally_ho/libs/tests/test_base.py index 886d67468..3dd26fca9 100644 --- a/tally_ho/libs/tests/test_base.py +++ b/tally_ho/libs/tests/test_base.py @@ -12,7 +12,6 @@ from tally_ho.apps.tally.models.center import Center from tally_ho.apps.tally.models.clearance import Clearance from tally_ho.apps.tally.models.office import Office -from tally_ho.apps.tally.models.quarantine_check import QuarantineCheck from tally_ho.apps.tally.models.reconciliation_form import ( ReconciliationForm, ) @@ -37,6 +36,9 @@ from tally_ho.libs.tests.fixtures.electrol_race_data import ( electrol_races ) +from tally_ho.libs.verify.quarantine_checks import ( + create_quarantine_checks as create_quarantine_checks_fn +) def configure_messages(request): @@ -238,20 +240,27 @@ def create_reconciliation_form( number_unused_ballots=1, number_spoiled_ballots=1, number_cancelled_ballots=1, - number_signatures_in_vr=1, - is_stamped=True): + number_of_voter_cards_in_the_ballot_box=1, + is_stamped=True, + total_of_cancelled_ballots_and_ballots_inside_box=1, + number_ballots_inside_and_outside_box=1, + ): return ReconciliationForm.objects.create( result_form=result_form, ballot_number_from=ballot_number_from, ballot_number_to=1, number_ballots_received=number_ballots_received, - number_signatures_in_vr=number_signatures_in_vr, + number_of_voter_cards_in_the_ballot_box=\ + number_of_voter_cards_in_the_ballot_box, number_unused_ballots=number_unused_ballots, number_spoiled_ballots=number_spoiled_ballots, number_cancelled_ballots=number_cancelled_ballots, number_ballots_outside_box=1, number_ballots_inside_box=number_ballots_inside_box, - number_ballots_inside_and_outside_box=1, + number_ballots_inside_and_outside_box=\ + number_ballots_inside_and_outside_box, + total_of_cancelled_ballots_and_ballots_inside_box=\ + total_of_cancelled_ballots_and_ballots_inside_box, number_unstamped_ballots=number_unstamped_ballots, number_invalid_votes=number_invalid_votes, number_valid_votes=number_valid_votes, @@ -266,14 +275,7 @@ def create_reconciliation_form( def create_quarantine_checks(quarantine_data): - for quarantine_check in quarantine_data: - QuarantineCheck.objects.get_or_create( - name=quarantine_check['name'], - method=quarantine_check['method'], - active=quarantine_check['active'], - value=quarantine_check['value'], - percentage=quarantine_check['percentage'] - ) + create_quarantine_checks_fn(quarantine_data=quarantine_data) def create_recon_forms(result_form, user): @@ -395,6 +397,7 @@ def result_form_data(result_form): 'number_unstamped_ballots': ['1'], 'number_ballots_inside_box': ['1'], 'number_ballots_inside_and_outside_box': ['1'], + 'total_of_cancelled_ballots_and_ballots_inside_box': ['2'], 'number_valid_votes': ['1'], 'number_unused_ballots': ['1'], 'number_spoiled_ballots': ['1'], @@ -404,7 +407,7 @@ def result_form_data(result_form): 'number_ballots_outside_box': ['1'], 'number_sorted_and_counted': ['1'], 'number_invalid_votes': ['1'], - 'number_signatures_in_vr': ['1'], + 'number_of_voter_cards_in_the_ballot_box': ['1'], 'ballot_number_to': ['1'], 'form-0-votes': ['1'], }) diff --git a/tally_ho/libs/tests/verify/test_quarantine_checks.py b/tally_ho/libs/tests/verify/test_quarantine_checks.py index 2d451c5ef..24618f0e3 100644 --- a/tally_ho/libs/tests/verify/test_quarantine_checks.py +++ b/tally_ho/libs/tests/verify/test_quarantine_checks.py @@ -1,87 +1,208 @@ -from tally_ho.libs.verify.quarantine_checks import\ - create_quarantine_checks, pass_overvote, pass_tampering +from django.conf import settings +from tally_ho.libs.models.enums.form_state import FormState +from tally_ho.libs.verify.quarantine_checks import ( + create_quarantine_checks, + pass_registrants_trigger, + pass_voter_cards_trigger, + pass_ballot_papers_trigger, + pass_ballot_inside_box_trigger, + pass_candidates_votes_trigger +) from tally_ho.libs.tests.test_base import create_candidates,\ create_center, create_reconciliation_form, create_result_form,\ - create_station, TestBase + create_station, TestBase, create_tally class TestQuarantineChecks(TestBase): def setUp(self): - create_quarantine_checks() + create_quarantine_checks(getattr(settings, 'QUARANTINE_DATA')) self._create_permission_groups() self._create_and_login_user() - def test_pass_overvote_true_no_registrants(self): - """Test pass overvote returns true with no registrants""" - center = create_center() - create_station(center=center) - result_form = create_result_form(center=center) - self.assertEqual(pass_overvote(result_form), True) - - def test_pass_overvote_true_no_recon(self): - """Test pass overvote returns true with no recon form""" - center = create_center() - create_station(center=center, - registrants=1) - result_form = create_result_form(center=center) - self.assertEqual(pass_overvote(result_form), True) + def test_pass_registrants_trigger(self): + """Test the pass_registrants_trigger function. - def test_pass_overvote_true(self): - """Test pass overvote returns true""" + This test checks that the total of cancelled ballots and + ballots inside the box does not exceed the number of + registered voters at the polling station. + """ + # Setup center = create_center() - create_station(center=center, - registrants=1) - result_form = create_result_form(center=center) - create_reconciliation_form(result_form, self.user) - self.assertEqual(pass_overvote(result_form), True) - - def test_pass_overvote_false(self): - """Test pass overvote returns false""" - center = create_center() - station = create_station(center=center, registrants=1) + station = create_station(center=center, + registrants=50) result_form = create_result_form( center=center, station_number=station.station_number) - create_reconciliation_form(result_form, - self.user, - number_unstamped_ballots=11) - self.assertEqual(pass_overvote(result_form), False) - def test_pass_tamper_true_no_registrants(self): - """Test pass tampering returns true with no registrants""" + # Test when there is no reconciliation form + self.assertTrue(pass_registrants_trigger(result_form)) + + recon_form =\ + create_reconciliation_form( + result_form=result_form, + user=self.user, + total_of_cancelled_ballots_and_ballots_inside_box=50 + ) + # Test when registrants are equal + self.assertTrue(pass_registrants_trigger(result_form)) + # Test when registrants are less + station.registrants = 40 + station.save() + result_form.reload() + self.assertFalse(pass_registrants_trigger(result_form)) + + # Test when registrants exceed + station.registrants = 60 + station.save() + result_form.reload() + self.assertTrue(pass_registrants_trigger(result_form)) + + # Test when number of ballots exceeds registrants + recon_form.total_of_cancelled_ballots_and_ballots_inside_box = 70 + recon_form.save() + result_form.reload() + self.assertFalse(pass_registrants_trigger(result_form)) + + def test_pass_voter_cards_trigger(self): + """Test the pass_voter_cards_trigger function. + + This test checks that the sum of cancelled ballots and + ballots inside the box equals the number of voter cards + in the ballot box. + """ + # Setup center = create_center() - create_station(center=center) - result_form = create_result_form(center=center) - self.assertEqual(pass_tampering(result_form), True) + station = create_station(center=center, registrants=100) + result_form = create_result_form( + center=center, + station_number=station.station_number, + ) + # Test when there is no reconciliation form + self.assertTrue(pass_voter_cards_trigger(result_form)) + + recon_form =\ + create_reconciliation_form( + result_form=result_form, + user=self.user, + total_of_cancelled_ballots_and_ballots_inside_box=50, + number_of_voter_cards_in_the_ballot_box=50, + ) + + # Test for equality + self.assertTrue(pass_voter_cards_trigger(result_form)) - def test_pass_tamper_true_no_recon(self): - """Test pass tampering returns true with no recon form""" + # Test for inequality + recon_form.number_of_voter_cards_in_the_ballot_box = 51 + recon_form.save() + result_form.reload() + self.assertFalse(pass_voter_cards_trigger(result_form)) + + def test_pass_ballot_papers_trigger(self): + """Test the pass_ballot_papers_trigger function. + + This test checks that the total number of ballots received + matches the total number of ballots found inside and outside + the ballot box. + """ + # Setup center = create_center() - create_station(center=center, - registrants=1) - result_form = create_result_form(center=center) - self.assertEqual(pass_tampering(result_form), True) + station = create_station(center=center, registrants=100) + result_form = create_result_form( + center=center, + station_number=station.station_number, + ) + # Test when there is no reconciliation form + self.assertTrue(pass_ballot_papers_trigger(result_form)) - def test_pass_tampering_true(self): - """Test pass tampering returns true""" + recon_form =\ + create_reconciliation_form( + result_form=result_form, + user=self.user, + number_ballots_received=100, + number_ballots_inside_and_outside_box=100, + ) + + # Test for equality + self.assertTrue(pass_ballot_papers_trigger(result_form)) + + # Test for inequality + recon_form.number_ballots_inside_and_outside_box = 99 + recon_form.save() + result_form.reload() + self.assertFalse(pass_ballot_papers_trigger(result_form)) + + def test_pass_ballot_inside_box_trigger(self): + """Test the pass_ballot_inside_box_trigger function. + + This test checks that the total number of ballots found + inside the ballot box equals the sum of unstamped ballots, + invalid votes, and valid votes. + """ + # Setup center = create_center() - create_station(center=center, - registrants=1) - result_form = create_result_form(center=center) - create_reconciliation_form(result_form, - self.user, - number_unstamped_ballots=0) - self.assertEqual(pass_tampering(result_form), True) - - def test_pass_tampering_true_diff(self): - """Test pass tampering returns true difference""" + station = create_station(center=center, registrants=100) + result_form = create_result_form( + center=center, + station_number=station.station_number, + ) + # Test when there is no reconciliation form + self.assertTrue(pass_ballot_inside_box_trigger(result_form)) + + recon_form =\ + create_reconciliation_form( + result_form=result_form, + user=self.user, + number_unstamped_ballots=30, + number_invalid_votes=20, + number_valid_votes=50, + number_ballots_inside_box=100, + ) + + # Test for equality + self.assertTrue(pass_ballot_inside_box_trigger(result_form)) + + # Test for inequality + recon_form.number_ballots_inside_box = 99 + recon_form.save() + result_form.reload() + self.assertFalse(pass_ballot_inside_box_trigger(result_form)) + + def test_pass_candidates_votes_trigger(self): + """Test the pass_candidates_votes_trigger function. + + This test checks that the total number of sorted and counted + ballots matches the total votes distributed among the candidates. + """ + # Setup center = create_center() - create_station(center=center, - registrants=1) - result_form = create_result_form(center=center) - create_candidates(result_form, self.user, num_results=1) - create_reconciliation_form(result_form, - self.user, - number_ballots_inside_box=250, - number_unstamped_ballots=0) - self.assertEqual(pass_tampering(result_form), True) + station = create_station(center=center, registrants=100) + result_form = create_result_form( + center=center, + station_number=station.station_number, + form_state=FormState.ARCHIVED + ) + # Test when there is no reconciliation form + self.assertTrue(pass_candidates_votes_trigger(result_form)) + + recon_form =\ + create_reconciliation_form( + result_form=result_form, + user=self.user, + number_valid_votes=200, + ) + votes = 100 + tally = create_tally() + tally.users.add(self.user) + create_candidates( + result_form, votes=votes, user=self.user, + num_results=1, tally=tally + ) + + # Test for equality + self.assertTrue(pass_candidates_votes_trigger(result_form)) + + # Test for inequality + recon_form.number_valid_votes = 100 + recon_form.save() + result_form.reload() + self.assertFalse(pass_candidates_votes_trigger(result_form)) diff --git a/tally_ho/libs/verify/quarantine_checks.py b/tally_ho/libs/verify/quarantine_checks.py index 8e33982d9..1ed26a2b9 100644 --- a/tally_ho/libs/verify/quarantine_checks.py +++ b/tally_ho/libs/verify/quarantine_checks.py @@ -2,21 +2,21 @@ from tally_ho.apps.tally.models.audit import Audit from tally_ho.apps.tally.models.quarantine_check import QuarantineCheck -from tally_ho.libs.models.enums.form_state import FormState -def create_quarantine_checks(): - for quarantine_check in getattr(settings, 'QUARANTINE_DATA'): - QuarantineCheck.objects.update_or_create( - name=quarantine_check['name'], - method=quarantine_check['method'], - defaults={'active': quarantine_check['active'], - 'value': quarantine_check['value'], - 'description': quarantine_check['description'], - 'percentage': quarantine_check['percentage']}, - ) - +def create_quarantine_checks(quarantine_data=None): + quarantine_data =\ + quarantine_data if quarantine_data is not None\ + else getattr(settings, 'QUARANTINE_DATA') + for quarantine_check in quarantine_data: + try: + QuarantineCheck.objects.get(method=quarantine_check['method']) + except QuarantineCheck.DoesNotExist: + QuarantineCheck.objects.create(**quarantine_check) +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def get_total_candidates_votes(result_form): """Calculate total candidates votes for the result form @@ -30,35 +30,29 @@ def get_total_candidates_votes(result_form): :returns: A Int of total candidates votes. """ - vote_list = () + # vote_list = () - for candidate in result_form.candidates: - votes = candidate.num_votes(result_form, result_form.form_state) - vote_list += (votes,) + # for candidate in result_form.candidates: + # votes = candidate.num_votes(result_form, result_form.form_state) + # vote_list += (votes,) - return sum(vote_list) + # return sum(vote_list) def quarantine_checks(): """Return tuples of (QuarantineCheck, validation_function).""" - all_methods = {'pass_overvote': - pass_overvote, - 'pass_tampering': - pass_tampering, - 'pass_ballots_number_validation': - pass_ballots_number_validation, - 'pass_signatures_validation': - pass_signatures_validation, - 'pass_ballots_inside_box_validation': - pass_ballots_inside_box_validation, - 'pass_sum_of_candidates_votes_validation': - pass_sum_of_candidates_votes_validation, - 'pass_invalid_ballots_percentage_validation': - pass_invalid_ballots_percentage_validation, - 'pass_turnout_percentage_validation': - pass_turnout_percentage_validation, - 'pass_percentage_of_votes_per_candidate_validation': - pass_percentage_of_votes_per_candidate_validation} + all_methods = { + 'pass_registrants_trigger': + pass_registrants_trigger, + 'pass_voter_cards_trigger': + pass_voter_cards_trigger, + 'pass_ballot_papers_trigger': + pass_ballot_papers_trigger, + 'pass_ballot_inside_box_trigger': + pass_ballot_inside_box_trigger, + 'pass_candidates_votes_trigger': + pass_candidates_votes_trigger, + } methods = [] quarantine_checks_methods =\ @@ -73,7 +67,9 @@ def quarantine_checks(): return zip(methods, checks) - +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_overvote(result_form): """Check to guard against overvoting. @@ -87,6 +83,42 @@ def pass_overvote(result_form): the number of potential voters minus the number of registrants plus N persons to accomodate staff and security. + :param result_form: The result form to check. + :returns: A boolean of true if passed, otherwise false. + """ + pass + # recon_form = result_form.reconciliationform + + # if not recon_form: + # return True + + # registrants = result_form.station.registrants if result_form.station\ + # else None + + # if registrants is None: + # return True + + # qc = QuarantineCheck.objects.get(method='pass_overvote') + # max_number_ballots = (qc.percentage / 100) * registrants + qc.value + + # return recon_form.number_ballots_used <= max_number_ballots + +def pass_registrants_trigger(result_form): + """Summation of recon fields number_cancelled_ballots and + number_ballots_inside_box must be less than or equal to the number of + registered voters at the station. + + If the `result_form` does not have a `reconciliation_form` this will + always return True. + + If the `station` for this `result_form` has an empty `registrants` field + this will always return True. + + Fails if the summation of recon fields number_cancelled_ballots and + number_ballots_inside_box exceeds the number of potential voters which is + the number of registered voters at the station plus N persons to + accommodate staff and security. + :param result_form: The result form to check. :returns: A boolean of true if passed, otherwise false. """ @@ -101,12 +133,94 @@ def pass_overvote(result_form): if registrants is None: return True - qc = QuarantineCheck.objects.get(method='pass_overvote') - max_number_ballots = (qc.percentage / 100) * registrants + qc.value + qc = QuarantineCheck.objects.get(method='pass_registrants_trigger') + potential_voters_num = registrants + qc.value + + return recon_form.total_of_cancelled_ballots_and_ballots_inside_box <=\ + potential_voters_num + +def pass_voter_cards_trigger(result_form): + """Summation of recon fields number_cancelled_ballots and + number_ballots_inside_box must be equal to + number_of_voter_cards_in_the_ballot_box + + If the `result_form` does not have a `reconciliation_form` this will + always return True. + + :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 + + return recon_form.total_of_cancelled_ballots_and_ballots_inside_box ==\ + recon_form.number_of_voter_cards_in_the_ballot_box + +def pass_ballot_papers_trigger(result_form): + """The total number of ballots received by the polling station must be + equal to the total number of ballots found inside and outside the + ballot box. + + If the `result_form` does not have a `reconciliation_form` this will + always return True. + + :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 + + return recon_form.number_ballots_received ==\ + recon_form.number_ballots_inside_and_outside_box + +def pass_ballot_inside_box_trigger(result_form): + """The total number of ballots found inside the ballot box must be + equal to the summation of the number of unstamped ballots and the + number of invalid votes (including the blanks) and + the number of valid votes + + If the `result_form` does not have a `reconciliation_form` this will + always return True. + + :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 + + return ( + recon_form.number_unstamped_ballots + + recon_form.number_invalid_votes + + recon_form.number_valid_votes + ) ==\ + recon_form.number_ballots_inside_box - return recon_form.number_ballots_used <= max_number_ballots +def pass_candidates_votes_trigger(result_form): + """The total valid votes must be equal to the total votes + distributed among the candidates. + If the `result_form` does not have a `reconciliation_form` this will + always return True. + :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 + + return result_form.num_votes == recon_form.number_valid_votes + +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_tampering(result_form): """Guard against errors and tampering with the form. @@ -120,23 +234,25 @@ def pass_tampering(result_form): :param result_form: The result form to check. :returns: A boolean of true if passed, otherwise false. """ - recon_form = result_form.reconciliationform + # recon_form = result_form.reconciliationform - if not recon_form: - return True + # if not recon_form: + # return True - num_votes = result_form.num_votes - number_ballots_expected = recon_form.number_ballots_expected - diff = abs(num_votes - number_ballots_expected) + # num_votes = result_form.num_votes + # number_ballots_expected = recon_form.number_ballots_expected + # diff = abs(num_votes - number_ballots_expected) # TODO Check if 'qc' variable must be QuarantineCheck object with method - # 'pass_tampering' or not - qc = QuarantineCheck.objects.get(method='pass_tampering') - scaled_tolerance = (qc.value / 100) * ( - num_votes + number_ballots_expected) / 2 - - return diff <= scaled_tolerance + # # 'pass_tampering' or not + # qc = QuarantineCheck.objects.get(method='pass_tampering') + # scaled_tolerance = (qc.value / 100) * ( + # num_votes + number_ballots_expected) / 2 + # return diff <= scaled_tolerance +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_ballots_number_validation(result_form): """Validate that the total number of received ballots equals the total of the ballots inside the box plus ballots outside the box. @@ -147,23 +263,25 @@ def pass_ballots_number_validation(result_form): :param result_form: The result form to check. :returns: A boolean of true if passed, otherwise false. """ - recon_form = result_form.reconciliationform + # recon_form = result_form.reconciliationform - if not recon_form: - return True - - ballots_inside_and_outside_the_box = ( - recon_form.number_ballots_inside_the_box + - recon_form.number_ballots_outside_the_box) - number_ballots_received = recon_form.number_ballots_received - diff = abs(number_ballots_received - ballots_inside_and_outside_the_box) - qc = QuarantineCheck.objects.get(method='pass_ballots_number_validation') - scaled_tolerance = (qc.value / 100) * ( - number_ballots_received + ballots_inside_and_outside_the_box) / 2 + # if not recon_form: + # return True - return diff <= scaled_tolerance + # ballots_inside_and_outside_the_box = ( + # recon_form.number_ballots_inside_the_box + + # recon_form.number_ballots_outside_the_box) + # number_ballots_received = recon_form.number_ballots_received + # diff = abs(number_ballots_received - ballots_inside_and_outside_the_box) + # qc = QuarantineCheck.objects.get(method='pass_ballots_number_validation') + # scaled_tolerance = (qc.value / 100) * ( + # number_ballots_received + ballots_inside_and_outside_the_box) / 2 + # return diff <= scaled_tolerance +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_signatures_validation(result_form): """Validate that the total number of signatures on the voter list for the voters who voted equals the number of ballots found in the ballot @@ -175,26 +293,29 @@ def pass_signatures_validation(result_form): :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 - - cancelled_ballots_and_ballots_inside_the_box = ( - recon_form.number_ballots_inside_the_box + - recon_form.number_cancelled_ballots) - number_signatures_in_vr = recon_form.number_signatures_in_vr - diff =\ - abs(number_signatures_in_vr - - cancelled_ballots_and_ballots_inside_the_box) - qc = QuarantineCheck.objects.get(method='pass_signatures_validation') - scaled_tolerance =\ - (qc.value / 100) * (number_signatures_in_vr + - cancelled_ballots_and_ballots_inside_the_box) / 2 - - return diff <= scaled_tolerance - - + # recon_form = result_form.reconciliationform + + # if not recon_form: + # return True + + # cancelled_ballots_and_ballots_inside_the_box = ( + # recon_form.number_ballots_inside_the_box + + # recon_form.number_cancelled_ballots) + # number_of_voter_cards_in_the_ballot_box =\ + # recon_form.number_of_voter_cards_in_the_ballot_box + # diff =\ + # abs(number_of_voter_cards_in_the_ballot_box - + # cancelled_ballots_and_ballots_inside_the_box) + # qc = QuarantineCheck.objects.get(method='pass_signatures_validation') + # scaled_tolerance =\ + # (qc.value / 100) * (number_of_voter_cards_in_the_ballot_box + + # cancelled_ballots_and_ballots_inside_the_box) / 2 + + # return diff <= scaled_tolerance + +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_ballots_inside_box_validation(result_form): """The total number of ballot papers inside the ballot box will be compared against the total of valid, invalid, and unstamped ballots. @@ -209,28 +330,30 @@ def pass_ballots_inside_box_validation(result_form): :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 - - # Number of ballots value entered by data entry clerk on the recon form - number_ballots_inside_box = recon_form.number_ballots_inside_box - - # The total of valid, invalid, and unstamped ballots - number_ballots_inside_the_box = recon_form.number_ballots_inside_the_box - diff =\ - abs(number_ballots_inside_box - - number_ballots_inside_the_box) - qc = QuarantineCheck.objects.get( - method='pass_ballots_inside_box_validation') - scaled_tolerance =\ - (qc.value / 100) * (number_ballots_inside_box + - number_ballots_inside_the_box) / 2 - - return diff <= scaled_tolerance - - + # recon_form = result_form.reconciliationform + + # if not recon_form: + # return True + + # # Number of ballots value entered by data entry clerk on the recon form + # number_ballots_inside_box = recon_form.number_ballots_inside_box + + # # The total of valid, invalid, and unstamped ballots + # number_ballots_inside_the_box = recon_form.number_ballots_inside_the_box + # diff =\ + # abs(number_ballots_inside_box - + # number_ballots_inside_the_box) + # qc = QuarantineCheck.objects.get( + # method='pass_ballots_inside_box_validation') + # scaled_tolerance =\ + # (qc.value / 100) * (number_ballots_inside_box + + # number_ballots_inside_the_box) / 2 + + # return diff <= scaled_tolerance + +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_sum_of_candidates_votes_validation(result_form): """The total votes for candidates should equal the valid ballots: after sorting the ballots inside the ballot box as valid and invalid, @@ -247,19 +370,21 @@ def pass_sum_of_candidates_votes_validation(result_form): :param result_form: The result form to check. :returns: A boolean of true if passed, otherwise false. """ - recon_form = result_form.reconciliationform + # recon_form = result_form.reconciliationform - if not recon_form: - return True - - total_candidates_votes = get_total_candidates_votes(result_form) - number_valid_votes = recon_form.number_valid_votes + # if not recon_form: + # return True - diff = total_candidates_votes - number_valid_votes + # total_candidates_votes = get_total_candidates_votes(result_form) + # number_valid_votes = recon_form.number_valid_votes - return diff > 0 + # diff = total_candidates_votes - number_valid_votes + # return diff > 0 +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_invalid_ballots_percentage_validation(result_form): """Validate the percentage of invalid ballots. @@ -272,21 +397,23 @@ def pass_invalid_ballots_percentage_validation(result_form): :param result_form: The result form to check. :returns: A boolean of true if passed, otherwise false. """ - recon_form = result_form.reconciliationform + # recon_form = result_form.reconciliationform - if not recon_form: - return True + # if not recon_form: + # return True - qc = QuarantineCheck.objects.get( - method='pass_invalid_ballots_percentage_validation') - 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_percentage <= allowed_invalid_ballots_percentage + # qc = QuarantineCheck.objects.get( + # method='pass_invalid_ballots_percentage_validation') + # 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_percentage <= allowed_invalid_ballots_percentage +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_turnout_percentage_validation(result_form): """Validate the turnout percentage. @@ -302,26 +429,28 @@ def pass_turnout_percentage_validation(result_form): :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 + # recon_form = result_form.reconciliationform - registrants = result_form.station.registrants if result_form.station\ - else None + # if not recon_form: + # return True - if registrants is None: - return True + # registrants = result_form.station.registrants if result_form.station\ + # else None - qc = QuarantineCheck.objects.get( - method='pass_turnout_percentage_validation') + # if registrants is None: + # return True - turnout_percentage = 100 * (recon_form.number_ballots_used / registrants) - allowed_turnout_percentage = qc.percentage + # qc = QuarantineCheck.objects.get( + # method='pass_turnout_percentage_validation') - return turnout_percentage <= allowed_turnout_percentage + # turnout_percentage = 100 * (recon_form.number_ballots_used / registrants) + # allowed_turnout_percentage = qc.percentage + # return turnout_percentage <= allowed_turnout_percentage +# Disabled: Awaiting client feedback for final removal. +# This function is temporarily inactive; it will be removed if the client +# confirms it is no longer required. def pass_percentage_of_votes_per_candidate_validation(result_form): """Validate that the percentage of votes per candidate of the total valid votes does not exceed a certain threshold. @@ -335,27 +464,27 @@ def pass_percentage_of_votes_per_candidate_validation(result_form): :param result_form: The result form to check. :returns: A boolean of true if passed, otherwise false. """ - recon_form = result_form.reconciliationform + # recon_form = result_form.reconciliationform - if not recon_form: - return True + # if not recon_form: + # return True - qc = QuarantineCheck.objects.get( - method='pass_percentage_of_votes_per_candidate_validation') - allowed_candidate_votes_percentage = qc.percentage - total_candidates_votes = get_total_candidates_votes(result_form) + # qc = QuarantineCheck.objects.get( + # method='pass_percentage_of_votes_per_candidate_validation') + # allowed_candidate_votes_percentage = qc.percentage + # total_candidates_votes = get_total_candidates_votes(result_form) - for candidate in result_form.candidates: - candidate_votes =\ - candidate.num_votes(result_form=result_form, - form_state=FormState.QUALITY_CONTROL) - candidate_votes_percentage =\ - 100 * (candidate_votes/total_candidates_votes) + # for candidate in result_form.candidates: + # candidate_votes =\ + # candidate.num_votes(result_form=result_form, + # form_state=FormState.QUALITY_CONTROL) + # candidate_votes_percentage =\ + # 100 * (candidate_votes/total_candidates_votes) - if candidate_votes_percentage > allowed_candidate_votes_percentage: - return False + # if candidate_votes_percentage > allowed_candidate_votes_percentage: + # return False - return True + # return True def check_quarantine(result_form, user): diff --git a/tally_ho/libs/views/exports.py b/tally_ho/libs/views/exports.py index 2290f1416..575100c46 100644 --- a/tally_ho/libs/views/exports.py +++ b/tally_ho/libs/views/exports.py @@ -100,7 +100,8 @@ def build_result_and_recon_output(result_form): 'cancelled ballots': recon.number_cancelled_ballots, 'spoilt ballots': recon.number_spoiled_ballots, 'unused ballots': recon.number_unused_ballots, - 'number of signatures': recon.number_signatures_in_vr, + 'number of voter cards in the ballot box': + recon.number_of_voter_cards_in_the_ballot_box, 'received ballots papers': recon.number_ballots_received, 'valid votes': recon.number_valid_votes, }) diff --git a/tally_ho/settings/common.py b/tally_ho/settings/common.py index 9937bc862..b53228740 100644 --- a/tally_ho/settings/common.py +++ b/tally_ho/settings/common.py @@ -196,118 +196,58 @@ # Quaritine trigger data QUARANTINE_DATA = [ - {'name': 'Trigger 1 - Guard against overvoting', + {'name': 'Registrants trigger', 'description': - str('Check to guard against overvoting. ' - 'If the result form does not have a reconciliation form this trigger ' - ' will always pass. ' - 'If the station for this result_form has an empty registrants field ' - 'this trigger will always pass. ' - 'Fails if the number of ballots reported to be used in a ' - 'station exceeds the number of potential voters minus the number ' - 'of registrants plus N persons to accomodate staff and security.'), - 'method': 'pass_overvote', + str('Summation of the number of cancelled ballots and ' + 'the number of ballots inside the box must be less than or equal to ' + 'the number of potential voters which is the number of registered ' + 'voters at the station plus N persons to accommodate staff and ' + 'security.'), + 'method': 'pass_registrants_trigger', 'active': True, - 'value': 10, - 'percentage': 90}, - {'name': 'Trigger 2 - Guard against errors and tampering with the form', + 'value': 0, + 'percentage': 0 + }, + {'name': 'Voter cards trigger', 'description': - str('Guard against errors and tampering with the form. ' - 'If the result form does not have a reconciliation form this trigger ' - 'will always pass. ' - 'Fails if the sum of the results section of the form does not equal ' - 'the number of ballots expected based on the calculation of the ' - 'key fields from the reconciliation form with a N% tolerance.'), - 'method': 'pass_tampering', + str('Summation of the number of cancelled ballots and ' + 'the number of ballots inside the box must be equal to ' + 'the number of voter cards in the ballot box.'), + 'method': 'pass_voter_cards_trigger', 'active': True, - 'value': 3, - 'percentage': 0}, - {'name': 'Trigger 3 - Validate total number of ballots used', - 'description': - str('Validate that the total number of received ballots equals the ' - 'total of the ballots inside the box plus ballots outside the box.' - ' If the result form does not have a reconciliation form this trigger' - ' will always pass.'), - 'method': 'pass_ballots_number_validation', - 'active': False, - 'value': 2, - 'percentage': 0}, - {'name': 'Trigger 4 - Validate number of signatures on the voter list', - 'description': - str('Validate that the total number of received ballots equals the ' - 'total of the ballots inside the box plus ballots outside the box. ' - 'If the result form does not have a reconciliation form this trigger ' - 'will always pass.'), - 'method': 'pass_signatures_validation', - 'active': False, - 'value': 2, - 'percentage': 0}, - {'name': 'Trigger 5 - Validate the total number of ballots inside the box', - 'description': - str('The total number of ballot papers inside the ballot box will be ' - 'compared against the total of valid, invalid, and unstamped ballots.' - ' If the result form does not have a reconciliation form this trigger' - ' will always pass. ' - 'Fails if the value of the number of ballots inside box from the ' - 'recon form does not equal the value of the recon property ' - 'number of ballots inside the box with an N% tolerance.'), - 'method': 'pass_ballots_inside_box_validation', - 'active': False, - 'value': 2, - 'percentage': 0}, - {'name': 'Trigger 6 - Validate sum of votes distributed to all candidates', - 'description': - str('The total votes for candidates should equal the valid ballots: ' - 'after sorting the ballots inside the ballot box as valid and ' - 'invalid, and unstamped. The above sum should equal the' - ' sum of all candidates votes. ' - 'If the result form does not have a reconciliation form this trigger ' - 'will always pass. ' - 'Fails if the value of the number of valid votes from the recon form ' - 'does not equal the sum of all candidates votes from the result form ' - 'with an N% tolerance.'), - 'method': 'pass_sum_of_candidates_votes_validation', - 'active': False, 'value': 0, - 'percentage': 0}, - {'name': 'Trigger 7 - Validate percentage of invalid ballots', + 'percentage': 0 + }, + {'name': 'Ballot papers trigger', 'description': - str('Validate the percentage of invalid ballots. ' - 'If the result form does not have a reconciliation form this trigger ' - 'will always pass. ' - 'Fails if the percentage of invalid ballots is greater than the this ' - 'trigger percentage value.'), - 'method': 'pass_invalid_ballots_percentage_validation', - 'active': False, + str('The total number of ballots received by the polling station must be' + ' equal to total number of ballots found inside and outside the ' + 'ballot box.'), + 'method': 'pass_ballot_papers_trigger', + 'active': True, 'value': 0, - 'percentage': 20}, - {'name': 'Trigger 8 - Validate turnout percentage', + 'percentage': 0 + }, + {'name': 'Ballot inside the box trigger', 'description': - str('Validate the turnout percentage. ' - 'If the result form does not have a reconciliation form this trigger ' - 'will always pass. ' - 'If the station for this result form has an empty registrants field ' - 'this trigger will always pass. ' - 'Fails if the turnout percentage is greater than the this ' - 'trigger percentage value.'), - 'method': 'pass_turnout_percentage_validation', - 'active': False, + str('The total number of ballots found inside the ballot box must be ' + 'equal to the summation of the number of unstamped ballots and the ' + 'number of invalid votes (including the blanks) and ' + 'the number of valid votes.'), + 'method': 'pass_ballot_inside_box_trigger', + 'active': True, 'value': 0, - 'percentage': 100}, - {'name': - 'Trigger 9 - Validate percentage of votes per candidate of total votes', + 'percentage': 0 + }, + {'name': 'Candidates votes trigger', 'description': - str('Validate that the percentage of votes per candidate of the total ' - 'valid votes does not exceed a certain threshold. ' - 'If the result form does not have a reconciliation form this trigger ' - 'will always pass. ' - 'Fails if the percentage of votes for a particular candidate ' - 'of the total valid votes is greater than the this trigger ' - 'percentage value.'), - 'method': 'pass_percentage_of_votes_per_candidate_validation', - 'active': False, + str('The total valid votes must be equal to the total votes ' + 'distributed among the candidates.'), + 'method': 'pass_candidates_votes_trigger', + 'active': True, 'value': 0, - 'percentage': 50}, + 'percentage': 0 + }, ] ELECTROL_RACES = [ diff --git a/tally_ho/urls.py b/tally_ho/urls.py index df299935c..78b4f1969 100644 --- a/tally_ho/urls.py +++ b/tally_ho/urls.py @@ -21,6 +21,7 @@ ) from tally_ho.apps.tally.views.data import ( electrol_race_list_view, + sub_constituency_list_view, ballot_list_view, center_list_view, form_list_view, @@ -467,6 +468,9 @@ re_path(r'^ajax/download-results/$', administrative_areas_reports.get_results, name='download-results'), + re_path(r'^ajax/download-sub-cons-list/$', + administrative_areas_reports.get_sub_cons_list, + name='download-sub-cons-list'), re_path(r'^super-administrator/results-(?P.*).csv/' r'(?P(\d+))/$', super_admin.ResultExportView.as_view(), @@ -503,6 +507,12 @@ r'(?P(\d+))$', super_admin.EditBallotView.as_view(), name='edit-ballot'), + re_path(r'^data/sub-cons-list-data/(?P(\d+))/$', + sub_constituency_list_view.SubConstituencyListDataView.as_view(), + name='sub-cons-list-data'), + re_path(r'^data/sub-cons-list/(?P(\d+))/$', + sub_constituency_list_view.SubConstituencyListView.as_view(), + name='sub-cons-list'), re_path(r'^data/electrol-race-list-data/(?P(\d+))/$', electrol_race_list_view.ElectrolRaceListDataView.as_view(), name='electrol-race-list-data'), diff --git a/tests/performance-tests/data.py b/tests/performance-tests/data.py index 30d0e55bc..3b882a8ae 100644 --- a/tests/performance-tests/data.py +++ b/tests/performance-tests/data.py @@ -53,13 +53,14 @@ 'signature_polling_station_chair': 'on', 'signature_dated': 'on', 'is_stamped': 'on', - 'number_signatures_in_vr': 10, + 'number_of_voter_cards_in_the_ballot_box': 10, 'number_unused_ballots': 0, 'number_spoiled_ballots': 0, 'number_cancelled_ballots': 0, 'number_ballots_outside_box': 0, 'number_ballots_inside_box': 10, 'number_ballots_inside_and_outside_box': 10, + 'total_of_cancelled_ballots_and_ballots_inside_box': 10, 'form-TOTAL_FORMS': 8, 'form-INITIAL_FORMS': 0, 'form-MIN_NUM_FORMS': 0, diff --git a/tests/performance-tests/data_entry1_clerk.py b/tests/performance-tests/data_entry1_clerk.py index 834b663a5..051f2773e 100644 --- a/tests/performance-tests/data_entry1_clerk.py +++ b/tests/performance-tests/data_entry1_clerk.py @@ -86,13 +86,14 @@ def enter_barcode(self): 'signature_polling_station_chair': 'on', 'signature_dated': 'on', 'is_stamped': 'on', - 'number_signatures_in_vr': '80', + 'number_of_voter_cards_in_the_ballot_box': '80', 'number_unused_ballots': '5', 'number_spoiled_ballots': '10', 'number_cancelled_ballots': '10', 'number_ballots_outside_box': '5', 'number_ballots_inside_box': '70', 'number_ballots_inside_and_outside_box': '100', + 'total_of_cancelled_ballots_and_ballots_inside_box': '80', 'form-TOTAL_FORMS': '8', 'form-INITIAL_FORMS': '0', 'form-MIN_NUM_FORMS': '0',