Skip to content

Commit

Permalink
Add mongo_uuid field to XForm model and populate it
Browse files Browse the repository at this point in the history
  • Loading branch information
rajpatel24 committed Oct 24, 2024
1 parent e0637af commit 47abf96
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ def test_xform_serializer_none(self):
'num_of_submissions': 0,
'attachment_storage_bytes': 0,
'kpi_asset_uid': '',
'mongo_uuid': '',
}
self.assertEqual(data, XFormSerializer(None).data)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.15 on 2024-10-24 20:16

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('logger', '0037_remove_xform_has_kpi_hooks_and_instance_posted_to_kpi'),
]

operations = [
migrations.AddField(
model_name='xform',
name='mongo_uuid',
field=models.CharField(
db_index=True, max_length=100, null=True, unique=True
),
),
]
3 changes: 3 additions & 0 deletions kobo/apps/openrosa/apps/logger/models/xform.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class XForm(AbstractTimeStampedModel):
last_submission_time = models.DateTimeField(blank=True, null=True)
has_start_time = models.BooleanField(default=False)
uuid = models.CharField(max_length=32, default='', db_index=True)
mongo_uuid = models.CharField(
max_length=100, null=True, unique=True, db_index=True
)

uuid_regex = re.compile(r'(<instance>.*?id="[^"]+">)(.*</instance>)(.*)',
re.DOTALL)
Expand Down
11 changes: 8 additions & 3 deletions kobo/apps/openrosa/apps/viewer/models/parsed_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,17 @@ def _get_paginated_and_sorted_cursor(cls, cursor, start, limit, sort):

def to_dict_for_mongo(self):
d = self.to_dict()

userform_id = (
self.instance.xform.mongo_uuid
if self.instance.xform.mongo_uuid
else f'{self.instance.xform.user.username}_{self.instance.xform.id_string}'
)

data = {
UUID: self.instance.uuid,
ID: self.instance.id,
self.USERFORM_ID: '%s_%s' % (
self.instance.xform.user.username,
self.instance.xform.id_string),
self.USERFORM_ID: userform_id,
ATTACHMENTS: _get_attachments_from_instance(self.instance),
self.STATUS: self.instance.status,
GEOLOCATION: [self.lat, self.lng],
Expand Down
7 changes: 6 additions & 1 deletion kobo/apps/project_ownership/tests/api/v2/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from rest_framework import status
from rest_framework.reverse import reverse

from kobo.apps.openrosa.apps.logger.models import XForm
from kobo.apps.project_ownership.models import Invite, InviteStatusChoices, Transfer
from kobo.apps.trackers.utils import update_nlp_counter
from kpi.constants import PERM_VIEW_ASSET
Expand Down Expand Up @@ -500,14 +501,18 @@ def test_data_accessible_to_new_user(self):
for attachment in response.data['results'][0]['_attachments']:
assert attachment['filename'].startswith('anotheruser/')

# Get the mongo_uuid for the transferred asset (XForm)
xform = XForm.objects.get(kpi_asset_uid=self.asset.uid)
mongo_uuid = xform.mongo_uuid

assert (
settings.MONGO_DB.instances.count_documents(
{'_userform_id': f'someuser_{self.asset.uid}'}
) == 0
)
assert (
settings.MONGO_DB.instances.count_documents(
{'_userform_id': f'anotheruser_{self.asset.uid}'}
{'_userform_id': mongo_uuid}
) == 1
)

Expand Down
2 changes: 2 additions & 0 deletions kobo/apps/project_ownership/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ def rewrite_mongo_userform_id(transfer: 'project_ownership.Transfer'):
if not transfer.asset.has_deployment:
return

transfer.asset.deployment.set_mongo_uuid()

if not transfer.asset.deployment.transfer_submissions_ownership(
old_owner.username
):
Expand Down
21 changes: 20 additions & 1 deletion kpi/deployment_backends/openrosa_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
except ImportError:
from backports.zoneinfo import ZoneInfo

import secrets
import string
import redis.exceptions
import requests
from defusedxml import ElementTree as DET
Expand Down Expand Up @@ -731,7 +733,9 @@ def get_validation_status(

@property
def mongo_userform_id(self):
return '{}_{}'.format(self.asset.owner.username, self.xform_id_string)
if self.xform.mongo_uuid:
return self.xform.mongo_uuid
return f'{self.asset.owner.username}_{self.xform_id_string}'

@staticmethod
def nlp_tracking_data(
Expand Down Expand Up @@ -1218,6 +1222,7 @@ def xform(self):
'attachment_storage_bytes',
'require_auth',
'uuid',
'mongo_uuid',
)
.select_related('user') # Avoid extra query to validate username below
.first()
Expand All @@ -1234,6 +1239,20 @@ def xform(self):
self._xform = xform
return self._xform

def generate_unique_key(self, length=20):

characters = string.ascii_letters + string.digits
# Generate a secure key with the specified length
return ''.join(secrets.choice(characters) for _ in range(length))

def set_mongo_uuid(self):
"""
Set the `mongo_uuid` for the associated XForm if it's not already set.
"""
if not self.xform.mongo_uuid:
self.xform.mongo_uuid = self.generate_unique_key()
self.xform.save(update_fields=['mongo_uuid'])

@property
def xform_id(self):
return self.xform.pk
Expand Down

0 comments on commit 47abf96

Please sign in to comment.