Skip to content

Commit

Permalink
Merge pull request #3316 from kobotoolbox/sync-media-on-redeploy
Browse files Browse the repository at this point in the history
Synchronize media files when redeploying a project with no changes
  • Loading branch information
jnm authored Jun 30, 2021
2 parents b6e4435 + 148f190 commit 480100f
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 8 deletions.
7 changes: 7 additions & 0 deletions jsapp/js/components/modalForms/formMedia.es6
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,13 @@ class FormMedia extends React.Component {
return (
<bem.FormView m='form-media'>
<bem.FormMedia>
{this.props.asset.deployment__active &&
<bem.FormView__cell m='warning'>
<i className='k-icon k-icon-alert' />
<p>{t('You must redeploy this form to see media changes.')}</p>
</bem.FormView__cell>
}

<bem.FormMedia__title>
<bem.FormMedia__label>
{t('Attach files')}
Expand Down
4 changes: 4 additions & 0 deletions jsapp/scss/components/_kobo.form-media.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
.form-media {
padding: 30px 40px;

.form-view__cell--warning {
margin-bottom: 24px;
}

.form-media__title {
display: inline-block;
position: relative;
Expand Down
3 changes: 3 additions & 0 deletions kpi/deployment_backends/kobocat_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,9 @@ def _upload_to_kc(file_):
expect_formid=False,
**kwargs)

file_.synced_with_backend = True
file_.save(update_fields=['synced_with_backend'])

# Process deleted files in case two entries contain the same file but
# one is flagged as deleted
asset_files = self.asset.asset_files.filter(
Expand Down
20 changes: 15 additions & 5 deletions kpi/deployment_backends/mixin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
# coding: utf-8
from kpi.constants import ASSET_TYPE_SURVEY
from kpi.exceptions import BadAssetTypeException
from kpi.models.asset_file import AssetFile
from kpi.tasks import sync_media_files
from .backends import DEPLOYMENT_BACKENDS
from .base_backend import BaseDeploymentBackend


class DeployableMixin:

def async_media_files(self, force=True):
"""
Synchronize form media files with deployment backend asynchronously
"""
if force or self.asset_files.filter(
file_type=AssetFile.FORM_MEDIA, synced_with_backend=False
).exists():
self.deployment.store_data(
{'status': self.deployment.STATUS_NOT_SYNCED}
)
self.save(create_version=False, adjust_content=False)
sync_media_files.delay(self.uid)

@property
def can_be_deployed(self):
return self.asset_type and self.asset_type == ASSET_TYPE_SURVEY
Expand All @@ -29,11 +43,7 @@ def deploy(self, backend=False, active=True):
self.deployment.redeploy(active=active)

self._mark_latest_version_as_deployed()
self.deployment.store_data(
{'status': self.deployment.STATUS_NOT_SYNCED}
)
self.save(create_version=False, adjust_content=False)
sync_media_files.delay(self.uid)
self.async_media_files()

else:
raise BadAssetTypeException(
Expand Down
21 changes: 21 additions & 0 deletions kpi/migrations/0037_add_sync_flag_to_asset_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 2.2.7 on 2021-06-25 17:54

from django.db import migrations, models
import kpi.models.import_export_task
import private_storage.fields
import private_storage.storage.s3boto3


class Migration(migrations.Migration):

dependencies = [
('kpi', '0036_add_date_deleted_field_to_asset_file'),
]

operations = [
migrations.AddField(
model_name='assetfile',
name='synced_with_backend',
field=models.BooleanField(default=False),
),
]
4 changes: 3 additions & 1 deletion kpi/models/asset_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class AssetFile(OpenRosaManifestInterface, models.Model):
content = PrivateFileField(upload_to=upload_to, max_length=380, null=True)
metadata = JSONBField(default=dict)
date_deleted = models.DateTimeField(null=True, default=None)
synced_with_backend = models.BooleanField(default=False)

def delete(self, using=None, keep_parents=False, force=False):
# Delete object and files on storage if `force` is True or file type
Expand All @@ -78,7 +79,8 @@ def delete(self, using=None, keep_parents=False, force=False):

# Otherwise, just flag the file as deleted.
self.date_deleted = timezone.now()
self.save(update_fields=['date_deleted'])
self.synced_with_backend = False
self.save(update_fields=['date_deleted', 'synced_with_backend'])

@property
def filename(self):
Expand Down
6 changes: 5 additions & 1 deletion kpi/serializers/v1/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ def update(self, instance, validated_data):
active=validated_data.get('active', deployment.active)
)
elif 'active' in validated_data:
active = validated_data['active']
# Set the `active` flag without touching the rest of the deployment
deployment.set_active(validated_data['active'])
deployment.set_active(active)
# If we (re)activate the asset, let's synchronize its media files
if active:
asset.async_media_files(force=False)

return deployment
3 changes: 2 additions & 1 deletion kpi/tests/api/v2/test_api_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,8 @@ def test_upload_form_media_bad_mime_type(self):
json_response = response.json()
expected_response = {
'metadata': ['Only `image`, `audio`, `video`, `text/csv`, '
'`application/xml` MIME types are allowed']
'`application/xml`, `application/zip` '
'MIME types are allowed']
}
assert json_response == expected_response

Expand Down

0 comments on commit 480100f

Please sign in to comment.