Skip to content

Commit

Permalink
Avoid duplicate submissions to be saved
Browse files Browse the repository at this point in the history
  • Loading branch information
noliveleger committed May 3, 2023
1 parent ba4cfbf commit 32e38a7
Show file tree
Hide file tree
Showing 14 changed files with 166 additions and 90 deletions.
20 changes: 19 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# coding: utf-8
import os
import pytest
import sys

import fakeredis
import pytest
from django.conf import settings
from mock import patch

from onadata.libs.utils.storage import rmdir, default_storage

Expand Down Expand Up @@ -78,6 +80,22 @@ def setup(request):
request.addfinalizer(_tear_down)


@pytest.fixture(scope='session', autouse=True)
def default_session_fixture(request):
"""
Globally patch redis_client with fake redis
"""
with patch(
'kobo_service_account.models.ServiceAccountUser.redis_client',
fakeredis.FakeStrictRedis(),
):
with patch(
'onadata.apps.django_digest_backends.cache.RedisCacheNonceStorage._get_cache',
fakeredis.FakeStrictRedis,
):
yield


def _tear_down():
print("\nCleaning testing environment...")
print('Removing MongoDB...')
Expand Down
1 change: 1 addition & 0 deletions dependencies/pip/dev_requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pytest-env
mongomock
mock
httmock
fakeredis[lua]
11 changes: 9 additions & 2 deletions dependencies/pip/dev_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile dependencies/pip/dev_requirements.in
#
Expand Down Expand Up @@ -166,6 +166,8 @@ et-xmlfile==1.1.0
# via openpyxl
executing==0.8.3
# via stack-data
fakeredis[lua]==2.11.2
# via -r dependencies/pip/dev_requirements.in
gdata-python3==3.0.1
# via -r dependencies/pip/requirements.in
httmock==1.4.0
Expand Down Expand Up @@ -194,6 +196,8 @@ jwcrypto==1.0
# via django-oauth-toolkit
kombu==5.2.4
# via celery
lupa==1.14.1
# via fakeredis
lxml==4.8.0
# via
# -r dependencies/pip/requirements.in
Expand Down Expand Up @@ -290,6 +294,7 @@ redis==4.2.2
# celery
# django-redis
# django-redis-sessions
# fakeredis
# kobo-service-account
requests==2.27.1
# via
Expand Down Expand Up @@ -320,6 +325,8 @@ six==1.16.0
# isodate
# mongomock
# python-dateutil
sortedcontainers==2.4.0
# via fakeredis
sqlparse==0.4.2
# via
# -r dependencies/pip/dev_requirements.in
Expand Down
4 changes: 2 additions & 2 deletions dependencies/pip/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile dependencies/pip/requirements.in
#
Expand Down
2 changes: 1 addition & 1 deletion onadata/apps/django_digest_backends/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
NONCE_NO_COUNT = '' # Needs to be something other than None to determine not set vs set to null


class RedisCacheNonceStorage():
class RedisCacheNonceStorage:
_blocking_timeout = 30

def _get_cache(self):
Expand Down
35 changes: 34 additions & 1 deletion onadata/apps/logger/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# coding: utf-8
from django.utils.translation import gettext as t

class ConflictingXMLHashInstanceError(Exception):
pass


class DuplicateInstanceError(Exception):

def __init__(self, message='Duplicate Instance'):
super().__init__(message)


class DuplicateUUIDError(Exception):
Expand All @@ -10,5 +18,30 @@ class FormInactiveError(Exception):
pass


class InstanceEmptyError(Exception):

def __init__(self, message='Empty instance'):
super().__init__(message)


class InstanceInvalidUserError(Exception):
def __init__(self, message='Could not determine the user'):
super().__init__(message)


class InstanceMultipleNodeError(Exception):
pass


class InstanceParseError(Exception):

def __init__(self, message='The instance could not be parsed'):
super().__init__(message)


class TemporarilyUnavailableError(Exception):
pass


class XLSFormError(Exception):
pass
1 change: 0 additions & 1 deletion onadata/apps/logger/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from onadata.apps.logger.models.instance import Instance
from onadata.apps.logger.models.survey_type import SurveyType
from onadata.apps.logger.models.xform import XForm
from onadata.apps.logger.xform_instance_parser import InstanceParseError
from onadata.apps.logger.models.note import Note
from onadata.apps.logger.models.daily_xform_submission_counter import (
DailyXFormSubmissionCounter,
Expand Down
1 change: 0 additions & 1 deletion onadata/apps/logger/models/instance.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# coding: utf-8
from hashlib import sha256

try:
from zoneinfo import ZoneInfo
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion onadata/apps/logger/models/xform.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from onadata.apps.logger.models.monthly_xform_submission_counter import (
MonthlyXFormSubmissionCounter,
)
from onadata.apps.logger.xform_instance_parser import XLSFormError
from onadata.apps.logger.exceptions import XLSFormError
from onadata.koboform.pyxform_utils import convert_csv_to_xls
from onadata.libs.constants import (
CAN_ADD_SUBMISSIONS,
Expand Down
4 changes: 2 additions & 2 deletions onadata/apps/logger/tests/test_simple_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.test import TestCase, RequestFactory
from pyxform import SurveyElementBuilder

from onadata.apps.logger.xform_instance_parser import DuplicateInstance
from onadata.apps.logger.exceptions import DuplicateInstanceError
from onadata.apps.viewer.models.data_dictionary import DataDictionary
from onadata.libs.utils.logger_tools import (
create_instance, safe_create_instance
Expand Down Expand Up @@ -39,7 +39,7 @@ def _submit_at_hour(self, hour):
'_time>' % hour
try:
create_instance(self.user.username, TempFileProxy(st_xml), [])
except DuplicateInstance:
except DuplicateInstanceError:
pass

def _submit_simple_yes(self):
Expand Down
31 changes: 2 additions & 29 deletions onadata/apps/logger/xform_instance_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,10 @@
from django.utils.translation import gettext as t
from xml.dom import minidom, Node

from onadata.apps.logger.exceptions import InstanceEmptyError
from onadata.libs.utils.common_tags import XFORM_ID_STRING


class XLSFormError(Exception):
pass


class DuplicateInstance(Exception):
def __str__(self):
return t("Duplicate Instance")


class InstanceInvalidUserError(Exception):
def __str__(self):
return t("Could not determine the user.")


class InstanceParseError(Exception):
def __str__(self):
return t("The instance could not be parsed.")


class InstanceEmptyError(InstanceParseError):
def __str__(self):
return t("Empty instance")


class InstanceMultipleNodeError(Exception):
pass


def get_meta_node_from_xml(
xml_str: str, meta_name: str
) -> Union[None, tuple[str, minidom.Document]]:
Expand Down Expand Up @@ -138,7 +111,7 @@ def clean_and_parse_xml(xml_string: str) -> minidom.Document:
def set_meta(xml_str: str, meta_name: str, new_value: str) -> str:

if not (node_and_root := get_meta_node_from_xml(xml_str, meta_name)):
raise ValueError(f"{meta_name} node not found.")
raise ValueError(f'{meta_name} node not found')

node, root = node_and_root

Expand Down
Loading

0 comments on commit 32e38a7

Please sign in to comment.