Skip to content

Commit 798703b

Browse files
committed
feat(project, handlers): refactor project singal handlers for views and add for tasks
Signed-off-by: David Wallace <[email protected]>
1 parent fc153d9 commit 798703b

File tree

8 files changed

+165
-101
lines changed

8 files changed

+165
-101
lines changed

rdmo/core/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,8 @@
332332

333333
PROJECT_SEND_INVITE = True
334334

335-
PROJECT_REMOVE_VIEWS = True
335+
PROJECT_VIEWS_SYNC = True
336+
PROJECT_TASKS_SYNC = True
336337

337338
PROJECT_CREATE_RESTRICTED = False
338339
PROJECT_CREATE_GROUPS = []

rdmo/projects/apps.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ class ProjectsConfig(AppConfig):
1010
def ready(self):
1111
from . import rules # noqa: F401
1212

13-
if settings.PROJECT_REMOVE_VIEWS:
14-
from . import handlers # noqa: F401
13+
if settings.PROJECT_VIEWS_SYNC:
14+
from .handlers import project_views # noqa: F401
15+
if settings.PROJECT_TASKS_SYNC:
16+
from .handlers import project_tasks # noqa: F401

rdmo/projects/handlers/__init__.py

Whitespace-only changes.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import logging
2+
3+
from django.contrib.auth.models import User
4+
from django.contrib.sites.models import Site
5+
from django.db.models.signals import m2m_changed
6+
from django.dispatch import receiver
7+
8+
from rdmo.projects.models import Membership, Project
9+
from rdmo.questions.models import Catalog
10+
from rdmo.tasks.models import Task
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
@receiver(m2m_changed, sender=Task.catalogs.through)
16+
def m2m_changed_task_catalog_signal(sender, instance, action, model, **kwargs):
17+
18+
task = instance
19+
# catalogs that were changed
20+
catalogs = model.objects.filter(pk__in=kwargs['pk_set'])
21+
if action in ('post_remove', 'post_clear'):
22+
# Remove the task from projects whose catalog is no longer linked to this task
23+
projects_to_change = Project.objects.filter(catalog__in=catalogs, tasks=task)
24+
for project in projects_to_change:
25+
project.tasks.remove(task)
26+
27+
elif action == 'post_add':
28+
# Add the task to projects whose catalog is now linked to this task
29+
projects_to_change = Project.objects.filter(catalog__in=task.catalogs.all()).exclude(tasks=task)
30+
for project in projects_to_change:
31+
project.tasks.add(task)
32+
33+
34+
@receiver(m2m_changed, sender=Task.sites.through)
35+
def m2m_changed_task_sites_signal(sender, instance, action, model, **kwargs):
36+
37+
task = instance
38+
sites = model.objects.filter(pk__in=kwargs['pk_set'])
39+
catalogs = task.catalogs.all() or Catalog.objects.all() # If no catalogs, consider all
40+
41+
if action in ('post_remove', 'post_clear'):
42+
# Remove the task from projects whose site is no longer linked to this task
43+
site_candidates = Site.objects.exclude(id__in=sites.values_list('id', flat=True))
44+
projects_to_change = Project.objects.filter(site__in=site_candidates, catalog__in=catalogs, tasks=task)
45+
for project in projects_to_change:
46+
project.tasks.remove(task)
47+
48+
elif action == 'post_add':
49+
# Add the task to projects whose site is now linked to this task
50+
site_candidates = sites
51+
projects_to_change = Project.objects.filter(site__in=site_candidates, catalog__in=catalogs).exclude(tasks=task)
52+
for project in projects_to_change:
53+
project.tasks.add(task)
54+
55+
56+
@receiver(m2m_changed, sender=Task.groups.through)
57+
def m2m_changed_task_groups_signal(sender, instance, action=None, **kwargs):
58+
59+
task = instance
60+
groups = task.groups.all()
61+
catalogs = task.catalogs.all() or Catalog.objects.all() # If no catalogs, consider all
62+
63+
if action in ('post_remove', 'post_clear'):
64+
# Remove the task from projects whose group is no longer linked to this task
65+
users = User.objects.exclude(groups__in=groups)
66+
memberships = Membership.objects.filter(role='owner', user__in=users).values_list('id', flat=True)
67+
projects_to_change = Project.objects.filter(memberships__in=memberships, catalog__in=catalogs, tasks=task)
68+
for project in projects_to_change:
69+
project.tasks.remove(task)
70+
71+
elif action == 'post_add':
72+
# Add the task to projects whose group is now linked to this task
73+
users = User.objects.filter(groups__in=groups)
74+
memberships = Membership.objects.filter(role='owner', user__in=users).values_list('id', flat=True)
75+
projects_to_change = Project.objects.filter(
76+
memberships__in=memberships, catalog__in=catalogs).exclude(tasks=task)
77+
for project in projects_to_change:
78+
project.tasks.add(task)

rdmo/projects/handlers.py renamed to rdmo/projects/handlers/project_views.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,25 @@
1313

1414

1515
@receiver(m2m_changed, sender=View.catalogs.through)
16-
def m2m_changed_view_catalog_signal(sender, instance, action, model, **kwargs):
17-
16+
def m2m_changed_view_catalog_signal(sender, instance, action, model, pk_set, **kwargs):
1817
view = instance
19-
# catalogs that were changed
20-
catalogs = model.objects.filter(pk__in=kwargs['pk_set'])
2118

22-
if action in ('post_remove', 'post_clear'):
19+
if action == 'post_remove':
20+
# catalogs that were changed
21+
catalogs = model.objects.filter(pk__in=pk_set)
2322
# Remove the view from projects whose catalog is no longer linked to this view
2423
projects_to_change = Project.objects.filter(catalog__in=catalogs, views=view)
2524
for project in projects_to_change:
2625
project.views.remove(view)
2726

27+
elif action == 'post_clear':
28+
# Remove the view from all projects that were using this view
29+
for project in Project.objects.filter(views=view):
30+
project.views.remove(view)
31+
2832
elif action == 'post_add':
2933
# Add the view to projects whose catalog is now linked to this view
30-
projects_to_change = Project.objects.filter(catalog__in=view.catalogs.all()).exclude(views=view)
31-
for project in projects_to_change:
34+
for project in Project.objects.filter(catalog__in=view.catalogs.all()):
3235
project.views.add(view)
3336

3437

rdmo/projects/tests/test_handlers.py

Lines changed: 0 additions & 91 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
3+
from rdmo.projects.models import Project
4+
from rdmo.questions.models import Catalog
5+
from rdmo.tasks.models import Task
6+
7+
task_id = 1
8+
9+
10+
def test_project_views_sync_when_adding_or_removing_a_catalog_to_or_from_a_task(db, settings):
11+
assert settings.PROJECT_TASKS_SYNC
12+
13+
# Setup: Create a catalog, a task, and a project using the catalog
14+
catalog = Catalog.objects.first()
15+
task = Task.objects.get(pk=task_id)
16+
task.catalogs.set([])
17+
project = Project.objects.create(title="Test Project", catalog=catalog)
18+
19+
# Initially, the project should not have the task
20+
assert task not in project.tasks.all()
21+
22+
# Add the catalog to the task
23+
task.catalogs.add(catalog)
24+
# After adding the catalog, the project should now include the task
25+
assert task in project.tasks.all()
26+
27+
# Remove the catalog from the task
28+
task.catalogs.remove(catalog)
29+
# After removing the catalog, the project should no longer include the task
30+
assert task not in project.tasks.all()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
3+
4+
from rdmo.projects.models import Project
5+
from rdmo.questions.models import Catalog
6+
from rdmo.views.models import View
7+
8+
9+
def test_project_views_sync_when_adding_or_removing_a_catalog_to_or_from_a_view(db, settings):
10+
assert settings.PROJECT_VIEWS_SYNC
11+
12+
# Setup: Create a catalog, a view, and a project using the catalog
13+
catalog = Catalog.objects.first()
14+
view = View.objects.get(pk=3)
15+
# view.catalogs.clear()
16+
project = Project.objects.create(title="Test Project", catalog=catalog)
17+
pr10 = Project.objects.get(pk=10)
18+
19+
# # Initially, the project should not have the view
20+
# assert view not in project.views.all()
21+
# assert view not in pr10.views.all()
22+
23+
## Tests for .add and .remove
24+
# Add the catalog to the view and assert that the project now includes the view
25+
view.catalogs.add(catalog)
26+
assert view in project.views.all()
27+
28+
# Remove the catalog from the view and assert that the project should no longer include the view
29+
view.catalogs.remove(catalog)
30+
assert view not in project.views.all()
31+
assert view not in pr10.views.all()
32+
33+
## Tests for .set and .clear
34+
# Add the catalog to the view and assert that the project now includes the view
35+
view.catalogs.set([catalog])
36+
assert view in project.views.all()
37+
38+
# Remove the catalog from the view and assert that the project should no longer include the view
39+
view.catalogs.clear()
40+
assert view not in project.views.all()
41+
assert view not in pr10.views.all()

0 commit comments

Comments
 (0)