Skip to content

Commit

Permalink
feat(projectHistoryLogs): log duplicated submissions TASK-1342 (#5387)
Browse files Browse the repository at this point in the history
### 📣 Summary
Record when submissions are duplicated. Record it just as a regular
submission.

### 💭 Notes
Add submission information to the request, then process as usual in
`ProjectHistoryLogs.create_from_request`.


### 👀 Preview steps

1. ℹ️ have at least 2 accounts and a project. Log in to the first
account
2. Give the second account the Manage Project permission
3. add a submission to the project and take note of the id
4. from the terminal, run `curl -X POST -H 'Authorization: Token
<account1_token>'
kf.kobo.local/api/v2/assets/<asset_uid>/data/<submission_id>/duplicate/`
5. reload the data table to make sure the submission was duplicated
6. go to `api/v2/assets/<asset_uid>/history`
7. 🟢 there should be a new PH log with action=`add-submission` and the
usual metadata, plus
```
"submission": {
    "submitted_by": <account1_username>
}
```
8. from the terminal, run `curl -X POST -H 'Authorization: Token
<account2_token>'
kf.kobo.local/api/v2/assets/<asset_uid>/data/<submission_id>/duplicate/`
(notice this is using account2's token)
9. reload the data table to make sure the submission was duplicated
again, with the new submission's "submitted_by" being account2.
10. go to `api/v2/assets/<asset_uid>/history`
11. 🟢 there should be a new PH log with action=`add-submission` and the
usual metadata, plus
```
"submission": {
    "submitted_by": <account2_username>
}
```
  • Loading branch information
rgraber authored Dec 20, 2024
1 parent c4de524 commit 570046d
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 0 deletions.
1 change: 1 addition & 0 deletions kobo/apps/audit_log/audit_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

class AuditAction(models.TextChoices):
ADD_MEDIA = 'add-media'
ADD_SUBMISSION = 'add-submission'
ALLOW_ANONYMOUS_SUBMISSIONS = 'allow-anonymous-submissions'
ARCHIVE = 'archive'
AUTH = 'auth'
Expand Down
65 changes: 65 additions & 0 deletions kobo/apps/audit_log/migrations/0014_add_submission_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Generated by Django 4.2.15 on 2024-12-19 19:37

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('audit_log', '0013_alter_auditlog_action'),
]

operations = [
migrations.AlterField(
model_name='auditlog',
name='action',
field=models.CharField(
choices=[
('add-media', 'Add Media'),
('add-submission', 'Add Submission'),
('allow-anonymous-submissions', 'Allow Anonymous Submissions'),
('archive', 'Archive'),
('auth', 'Auth'),
('clone-permissions', 'Clone Permissions'),
('connect-project', 'Connect Project'),
('create', 'Create'),
('delete', 'Delete'),
('delete-media', 'Delete Media'),
('delete-service', 'Delete Service'),
('deploy', 'Deploy'),
('disable-sharing', 'Disable Sharing'),
(
'disallow-anonymous-submissions',
'Disallow Anonymous Submissions',
),
('disconnect-project', 'Disconnect Project'),
('enable-sharing', 'Enable Sharing'),
('export', 'Export'),
('in-trash', 'In Trash'),
('modify-imported-fields', 'Modify Imported Fields'),
('modify-service', 'Modify Service'),
('modify-sharing', 'Modify Sharing'),
('modify-user-permissions', 'Modify User Permissions'),
('put-back', 'Put Back'),
('redeploy', 'Redeploy'),
('register-service', 'Register Service'),
('remove', 'Remove'),
('replace-form', 'Replace Form'),
('share-form-publicly', 'Share Form Publicly'),
('share-data-publicly', 'Share Data Publicly'),
('unarchive', 'Unarchive'),
('unshare-form-publicly', 'Unshare Form Publicly'),
('unshare-data-publicly', 'Unshare Data Publicly'),
('update', 'Update'),
('update-content', 'Update Content'),
('update-name', 'Update Name'),
('update-settings', 'Update Settings'),
('update-qa', 'Update Qa'),
('transfer', 'Transfer'),
],
db_index=True,
default='delete',
max_length=30,
),
),
]
21 changes: 21 additions & 0 deletions kobo/apps/audit_log/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ def create_from_request(cls, request: WSGIRequest):
'asset-permission-assignment-list': cls._create_from_permissions_request,
'asset-permission-assignment-clone': cls._create_from_clone_permission_request, # noqa
'project-ownership-invite-list': cls._create_from_ownership_transfer,
'submission-duplicate': cls._create_from_duplicate_request,
}
url_name = request.resolver_match.url_name
method = url_name_to_action.get(url_name, None)
Expand Down Expand Up @@ -567,6 +568,26 @@ def _create_from_detail_request(cls, request):
metadata=full_metadata,
)

@classmethod
def _create_from_duplicate_request(cls, request):
asset_uid = request.resolver_match.kwargs['parent_lookup_asset']
updated_data = request.updated_data
metadata = {
'ip_address': get_client_ip(request),
'source': get_human_readable_client_user_agent(request),
'asset_uid': asset_uid,
'log_subtype': PROJECT_HISTORY_LOG_PROJECT_SUBTYPE,
'submission': {
'submitted_by': updated_data['submitted_by']
}
}
ProjectHistoryLog.objects.create(
object_id=updated_data['asset_id'],
action=AuditAction.ADD_SUBMISSION,
user=request.user,
metadata=metadata
)

@classmethod
def _create_from_export_request(cls, request):
cls._related_request_base(request, None, AuditAction.EXPORT, None, None)
Expand Down
47 changes: 47 additions & 0 deletions kobo/apps/audit_log/tests/test_project_history_logs.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import base64
import copy
import json
import uuid
from unittest.mock import patch

import jsonschema.exceptions
import responses
from ddt import data, ddt, unpack
from django.conf import settings
from django.test import override_settings
from django.urls import reverse
from rest_framework.response import Response
Expand All @@ -21,6 +23,7 @@
CLONE_ARG_NAME,
PERM_ADD_SUBMISSIONS,
PERM_CHANGE_SUBMISSIONS,
PERM_MANAGE_ASSET,
PERM_PARTIAL_SUBMISSIONS,
PERM_VIEW_ASSET,
PERM_VIEW_SUBMISSIONS,
Expand Down Expand Up @@ -59,6 +62,10 @@ def setUp(self):
self.detail_url = 'asset-detail'
self.deployment_url = 'asset-deployment'

def tearDown(self):
# clean up mongo
settings.MONGO_DB.instances.delete_many({})

def _check_common_metadata(self, metadata_dict, expected_subtype):
self.assertEqual(metadata_dict['asset_uid'], self.asset.uid)
self.assertEqual(metadata_dict['ip_address'], '127.0.0.1')
Expand Down Expand Up @@ -1448,3 +1455,43 @@ def test_no_log_created_for_non_project_transfer(self):
},
)
self.assertEqual(ProjectHistoryLog.objects.count(), 0)

@data('admin', 'someuser')
def test_log_created_for_duplicate_submission(self, duplicating_user):
uuid_ = uuid.uuid4()
# add a submission by 'admin'
submission_data = {
'q1': 'answer',
'q2': 'answer',
'meta/instanceID': f'uuid:{uuid_}',
'_uuid': str(uuid_),
'_submitted_by': 'admin',
}
self.asset.deploy(backend='mock')

self.asset.deployment.mock_submissions([submission_data])
submissions = self.asset.deployment.get_submissions(
self.asset.owner, fields=['_id']
)
submission = submissions[0]
submission_url = reverse(
self._get_endpoint('submission-duplicate'),
kwargs={
'parent_lookup_asset': self.asset.uid,
'pk': submission['_id'],
},
)
# whoever performs the duplication request will be considered the submitter
# of the new submission, even if the original was submitted by someone else
user = User.objects.get(username=duplicating_user)
self.asset.assign_perm(user_obj=user, perm=PERM_MANAGE_ASSET)
self.client.force_login(user)

metadata = self._base_project_history_log_test(
method=self.client.post,
url=submission_url,
request_data={},
expected_action=AuditAction.ADD_SUBMISSION,
expected_subtype=PROJECT_HISTORY_LOG_PROJECT_SUBTYPE,
)
self.assertEqual(metadata['submission']['submitted_by'], duplicating_user)
2 changes: 2 additions & 0 deletions kobo/apps/audit_log/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ class AllProjectHistoryLogViewSet(AuditLogViewSet):
available actions:
> add-media
> add-submission
> allow-anonymous-submissions
> archive
> clone-permissions
Expand Down Expand Up @@ -673,6 +674,7 @@ class ProjectHistoryLogViewSet(
available actions:
> add-media
> add-submission
> allow-anonymous-submissions
> archive
> clone-permissions
Expand Down
4 changes: 4 additions & 0 deletions kpi/views/v2/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ def duplicate(self, request, pk, *args, **kwargs):
deployment.copy_submission_extras(
original_submission['_uuid'], duplicate_submission['_uuid']
)
request._request.updated_data = {
'asset_id': self.asset.id,
'submitted_by': duplicate_submission['_submitted_by'],
}
response = {
'data': duplicate_submission,
'content_type': 'application/json',
Expand Down

0 comments on commit 570046d

Please sign in to comment.