Skip to content

Commit

Permalink
add notify_email_zone_status setting (#1916, #1939), fix display_conf…
Browse files Browse the repository at this point in the history
…ig scope
  • Loading branch information
mikkonie committed Sep 13, 2024
1 parent 7032cb2 commit c2cba7c
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Added
- Python v3.11 support (#1922, #1978)
- **Landingzones**
- REST API list view pagination (#1994)
- ``notify_email_zone_status`` user app setting (#1939)
- Tests for taskflow tasks (#1916)
- **Samplesheets**
- REST API list view pagination (#1994)
- ``notify_email_irods_request`` user app setting (#1939)
Expand Down
11 changes: 10 additions & 1 deletion landingzones/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,16 @@ class ProjectAppPlugin(
'new files are uploaded from landing zones',
'user_modifiable': True,
'default': True,
}
},
'notify_email_zone_status': {
'scope': SODAR_CONSTANTS['APP_SETTING_SCOPE_USER'],
'type': 'BOOLEAN',
'default': True,
'label': 'Receive email for landing zone status updates',
'description': 'Receive email notifications for status changes in '
'your landing zones',
'user_modifiable': True,
},
}

#: Iconify icon
Expand Down
17 changes: 7 additions & 10 deletions landingzones/tasks_taskflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ def set_status(cls, zone, flow_name, status, status_info, extra_data=None):
settings.PROJECTROLES_SEND_EMAIL
and flow_name == 'landing_zone_move'
and not validate_only
and app_settings.get(
APP_NAME, 'notify_email_zone_status', user=zone.user
)
):
try:
cls._send_owner_move_email(zone)
Expand All @@ -304,16 +307,10 @@ def set_status(cls, zone, flow_name, status, status_info, extra_data=None):
and zone.status == ZONE_STATUS_MOVED
and file_count > 0
):
members = list(
set(
[
r.user
for r in zone.project.get_roles()
if r.user != zone.user
]
)
)
for member in members:
members = [
r.user for r in zone.project.get_roles() if r.user != zone.user
]
for member in list(set(members)):
if app_alerts:
try:
cls._add_member_move_alert(
Expand Down
1 change: 1 addition & 0 deletions landingzones/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def make_landing_zone(
'description': description,
'user_message': user_message,
'status': status,
'status_info': DEFAULT_STATUS_INFO[status],
'configuration': configuration,
'config_data': config_data,
}
Expand Down
229 changes: 229 additions & 0 deletions landingzones/tests/test_tasks_taskflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
"""Taskflow task tests for the landingzones app"""

# NOTE: These do not NOT require running with taskflow and iRODS

from django.core import mail
from django.test import override_settings

# Projectroles dependency
from projectroles.app_settings import AppSettingAPI

# Appalerts dependency
from appalerts.models import AppAlert

import landingzones.constants as lc
from landingzones.tasks_taskflow import SetLandingZoneStatusTask
from landingzones.tests.test_views import TestViewsBase


app_settings = AppSettingAPI()


# Local constants
APP_NAME = 'landingzones'
TASK_NAME = 'set landing zone status'


class TestSetLandingZoneStatusTask(TestViewsBase):
"""Tests for SetLandingZoneStatusTask"""

def _get_task(self, force_fail=False):
"""Initialize and return task"""
return SetLandingZoneStatusTask(TASK_NAME, self.project, force_fail)

def _assert_owner_alert(self, count, name='zone_move'):
"""Assert owner alert count"""
self.assertEqual(
AppAlert.objects.filter(
app_plugin__name=APP_NAME,
alert_name=name,
user=self.landing_zone.user,
project=self.project,
).count(),
count,
)

def _assert_member_alerts(self, count):
"""Assert member alert count"""
self.assertEqual(
AppAlert.objects.filter(
app_plugin__name=APP_NAME,
alert_name='zone_move_member',
project=self.project,
).count(),
count,
)

def setUp(self):
super().setUp()
self.task_kw = {
'landing_zone': self.landing_zone,
'flow_name': 'landing_zone_move',
'status': lc.ZONE_STATUS_MOVED,
'status_info': lc.DEFAULT_STATUS_INFO[lc.ZONE_STATUS_MOVED],
'extra_data': {'file_count': 1},
}

def test_execute(self):
"""Test SetLandingZoneStatusTask execute()"""
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self.assertEqual(
self.landing_zone.status_info,
lc.DEFAULT_STATUS_INFO[lc.ZONE_STATUS_ACTIVE],
)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)

self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()

self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_MOVED)
self.assertEqual(
self.landing_zone.status_info,
lc.DEFAULT_STATUS_INFO[lc.ZONE_STATUS_MOVED],
)
self._assert_owner_alert(1)
self._assert_member_alerts(1)
self.assertEqual(len(mail.outbox), 2)
self.assertEqual(
AppAlert.objects.filter(alert_name='zone_move').first().level,
'SUCCESS',
)

@override_settings(PROJECTROLES_SEND_EMAIL=False)
def test_execute_disable_email(self):
"""Test execute() with email disabled"""
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)
self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_MOVED)
self._assert_owner_alert(1)
self._assert_member_alerts(1)
self.assertEqual(len(mail.outbox), 0)

def test_execute_disable_member_nofify(self):
"""Test execute() with member notify disabled"""
app_settings.set(
APP_NAME, 'member_notify_move', False, project=self.project
)
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)
self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_MOVED)
self._assert_owner_alert(1)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].recipients(), [self.user.email])

def test_execute_disable_owner_nofify(self):
"""Test execute() with owner notify disabled"""
app_settings.set(
APP_NAME,
'notify_email_zone_status',
False,
user=self.landing_zone.user,
)
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)
self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_MOVED)
self._assert_owner_alert(1)
self._assert_member_alerts(1)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].recipients(), [self.user_contributor.email]
)

def test_execute_moved_no_files(self):
"""Test execute() with a busy status"""
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)
self.task_kw['extra_data'] = {'file_count': 0}
self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_MOVED)
# No alerts or emails should be set
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)

def test_execute_busy(self):
"""Test execute() with busy status"""
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)
self.task_kw['status'] = lc.ZONE_STATUS_MOVING
self.task_kw['status_info'] = lc.DEFAULT_STATUS_INFO[
lc.ZONE_STATUS_MOVING
]
self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_MOVING)
self.assertEqual(
self.landing_zone.status_info,
lc.DEFAULT_STATUS_INFO[lc.ZONE_STATUS_MOVING],
)
# No alerts or emails should be set
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)

def test_execute_failed(self):
"""Test execute() with FAILED status"""
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)
self.task_kw['status'] = lc.ZONE_STATUS_FAILED
self.task_kw['status_info'] = lc.DEFAULT_STATUS_INFO[
lc.ZONE_STATUS_FAILED
]
self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_FAILED)
self.assertEqual(
self.landing_zone.status_info,
lc.DEFAULT_STATUS_INFO[lc.ZONE_STATUS_FAILED],
)
self._assert_owner_alert(1)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
AppAlert.objects.filter(alert_name='zone_move').first().level,
'DANGER',
)

def test_execute_validate(self):
"""Test execute() in validate mode"""
self.assertEqual(self.landing_zone.status, lc.ZONE_STATUS_ACTIVE)
self._assert_owner_alert(0)
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0)
self.task_kw['status'] = lc.ZONE_STATUS_ACTIVE
self.task_kw['status_info'] = [
lc.DEFAULT_STATUS_INFO[lc.ZONE_STATUS_ACTIVE]
]
self.task_kw['extra_data'] = {'validate_only': True}
self._get_task().execute(**self.task_kw)
self.landing_zone.refresh_from_db()
self.task_kw['status'] = lc.ZONE_STATUS_ACTIVE
self.task_kw['status_info'] = [
lc.DEFAULT_STATUS_INFO[lc.ZONE_STATUS_ACTIVE]
]
self._assert_owner_alert(0)
self._assert_owner_alert(1, name='zone_validate')
self._assert_member_alerts(0)
self.assertEqual(len(mail.outbox), 0) # No email sent on validate
5 changes: 4 additions & 1 deletion samplesheets/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
PROJECT_ACTION_UPDATE = SODAR_CONSTANTS['PROJECT_ACTION_UPDATE']
APP_SETTING_SCOPE_PROJECT = SODAR_CONSTANTS['APP_SETTING_SCOPE_PROJECT']
APP_SETTING_SCOPE_USER = SODAR_CONSTANTS['APP_SETTING_SCOPE_USER']
APP_SETTING_SCOPE_PROJECT_USER = SODAR_CONSTANTS[
'APP_SETTING_SCOPE_PROJECT_USER'
]

# Local constants
SHEETS_INFO_SETTINGS = [
Expand Down Expand Up @@ -124,7 +127,7 @@ class ProjectAppPlugin(
'default': True,
},
'display_config': {
'scope': APP_SETTING_SCOPE_USER,
'scope': APP_SETTING_SCOPE_PROJECT_USER,
'type': 'JSON',
'label': 'Sample sheet display configuration',
'description': 'User specific JSON configuration for column '
Expand Down

0 comments on commit c2cba7c

Please sign in to comment.