Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove views from existing projects on view update #431

Merged
merged 14 commits into from
Nov 17, 2022
Merged
2 changes: 2 additions & 0 deletions rdmo/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@

PROJECT_SEND_INVITE = True

PROJECT_REMOVE_VIEWS = True

NESTED_PROJECTS = True

OPTIONSET_PROVIDERS = []
Expand Down
4 changes: 4 additions & 0 deletions rdmo/projects/apps.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.apps import AppConfig
from django.conf import settings
from django.utils.translation import gettext_lazy as _


Expand All @@ -8,3 +9,6 @@ class ProjectsConfig(AppConfig):

def ready(self):
from . import rules

if settings.PROJECT_REMOVE_VIEWS:
from . import handlers
Comment on lines +13 to +14
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the implementation into the projects app. I think this makes sense as you suggested. Furthermore, I also moved it from signals.py to handlers.py. However, I am not happy with the linking into the projects/apps.py due to the configuration flag.

60 changes: 60 additions & 0 deletions rdmo/projects/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import logging

from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.db.models.signals import m2m_changed
from django.dispatch import receiver

from rdmo.questions.models import Catalog
from rdmo.projects.models import Project, Membership
from rdmo.views.models import View

logger = logging.getLogger(__name__)


@receiver(m2m_changed, sender=View.catalogs.through)
def m2m_changed_view_catalog_signal(sender, instance, **kwargs):
catalogs = instance.catalogs.all()

if catalogs:
catalog_candidates = Catalog.objects.exclude(id__in=[catalog.id for catalog in catalogs])

# Remove catalog candidates for all sites
projects = Project.objects.filter(catalog__in=catalog_candidates, views=instance)
for proj in projects:
proj.views.remove(instance)


@receiver(m2m_changed, sender=View.sites.through)
def m2m_changed_view_sites_signal(sender, instance, **kwargs):
sites = instance.sites.all()
catalogs = instance.catalogs.all()

if sites:
site_candidates = Site.objects.exclude(id__in=[site.id for site in sites])
if not catalogs:
# if no catalogs are selected, update all
catalogs = Catalog.objects.all()

# Restrict chosen catalogs for chosen sites
projects = Project.objects.filter(site__in=site_candidates, catalog__in=catalogs, views=instance)
for project in projects:
project.views.remove(instance)


@receiver(m2m_changed, sender=View.groups.through)
def m2m_changed_view_groups_signal(sender, instance, **kwargs):
groups = instance.groups.all()
catalogs = instance.catalogs.all()

if groups:
users = User.objects.exclude(groups__in=groups)
memberships = [membership.id for membership in Membership.objects.filter(role='owner', user__in=users)]
if not catalogs:
# if no catalogs are selected, update all
catalogs = Catalog.objects.all()

# Restrict chosen catalogs for chosen groups
projects = Project.objects.filter(memberships__in=list(memberships), catalog__in=catalogs, views=instance)
for project in projects:
project.views.remove(instance)
60 changes: 60 additions & 0 deletions rdmo/projects/tests/test_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import itertools
import pytest

from django.contrib.auth.models import Group
from django.contrib.sites.models import Site

from rdmo.projects.models import Project
from rdmo.questions.models import Catalog
from rdmo.views.models import View

view_update_tests = [
# tuples of: view_id, sites, catalogs, groups, project_id, project_exists
('3', [], [], [], '10', True),
('3', [2], [], [], '10', False),
('3', [1, 2, 3], [], [], '10', True),
('3', [], [2], [], '10', False),
('3', [2], [2], [], '10', False),
('3', [1, 2, 3], [2], [], '10', False),
('3', [], [1, 2], [], '10', True),
('3', [2], [1, 2], [], '10', False),
('3', [1, 2, 3], [1, 2], [], '10', True),

('3', [], [], [1], '10', False),
('3', [2], [], [1], '10', False),
('3', [1, 2, 3], [], [1], '10', False),
('3', [], [2], [1], '10', False),
('3', [2], [2], [1], '10', False),
('3', [1, 2, 3], [2], [1], '10', False),
('3', [], [1, 2], [1], '10', False),
('3', [2], [1, 2], [1], '10', False),
('3', [1, 2, 3], [1, 2], [1], '10', False),

('3', [], [], [1, 2, 3, 4], '10', True),
('3', [2], [], [1, 2, 3, 4], '10', False),
('3', [1, 2, 3], [], [1, 2, 3, 4], '10', True),
('3', [], [2], [1, 2, 3, 4], '10', False),
('3', [2], [2], [1, 2, 3, 4], '10', False),
('3', [1, 2, 3], [2], [1, 2, 3, 4], '10', False),
('3', [], [1, 2], [1, 2, 3, 4], '10', True),
('3', [2], [1, 2], [1, 2, 3, 4], '10', False),
('3', [1, 2, 3], [1, 2], [1, 2, 3, 4], '10', True)
]

@pytest.mark.parametrize('view_id,sites,catalogs,groups,project_id,project_exists', view_update_tests)
def test_update_projects(db, view_id, sites, catalogs, groups, project_id, project_exists):
view = View.objects.get(pk=view_id)

view.sites.set(Site.objects.filter(pk__in=sites))
view.catalogs.set(Catalog.objects.filter(pk__in=catalogs))
view.groups.set(Group.objects.filter(pk__in=groups))

assert sorted(list(itertools.chain.from_iterable(view.sites.all().values_list('pk')))) == sites
assert sorted(list(itertools.chain.from_iterable(view.catalogs.all().values_list('pk')))) == catalogs
assert sorted(list(itertools.chain.from_iterable(view.groups.all().values_list('pk')))) == groups

if not project_exists:
with pytest.raises(Project.DoesNotExist):
Project.objects.filter(views=view).get(pk=project_id)
else:
assert Project.objects.filter(views=view).get(pk=project_id)
18 changes: 9 additions & 9 deletions rdmo/projects/tests/test_view_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,35 @@
)

view_project_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'manager': [1, 3, 5, 7],
'author': [1, 3, 5, 8],
'guest': [1, 3, 5, 9],
'api': [1, 2, 3, 4, 5],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

change_project_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'manager': [1, 3, 5, 7],
'api': [1, 2, 3, 4, 5],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

delete_project_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'api': [1, 2, 3, 4, 5],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

export_project_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'manager': [1, 3, 5, 7],
'api': [1, 2, 3, 4, 5],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

projects = [1, 2, 3, 4, 5, 6, 7, 8, 9]
projects = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

export_formats = ('rtf', 'odt', 'docx', 'html', 'markdown', 'tex', 'pdf')

Expand Down
8 changes: 4 additions & 4 deletions rdmo/projects/tests/test_viewset_issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@
)

view_issue_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'manager': [1, 3, 5],
'author': [1, 3, 5],
'guest': [1, 3, 5],
'api': [1, 2, 3, 4, 5],
'site': [1, 2, 3, 4, 5]
'api': [1, 2, 3, 4, 5, 10],
'site': [1, 2, 3, 4, 5, 10]
}

urlnames = {
'list': 'v1-projects:issue-list',
'detail': 'v1-projects:issue-detail'
}

projects = [1, 2, 3, 4, 5]
projects = [1, 2, 3, 4, 5, 10]
issues = [1, 2, 3, 4]

site_id = 1
Expand Down
8 changes: 4 additions & 4 deletions rdmo/projects/tests/test_viewset_membership.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@
)

view_membership_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'manager': [1, 3, 5, 7],
'author': [1, 3, 5, 8],
'guest': [1, 3, 5, 9],
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

urlnames = {
'list': 'v1-projects:membership-list',
'detail': 'v1-projects:membership-detail'
}

projects = [1, 2, 3, 4, 5, 6, 7, 8, 9]
projects = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
memberships = [1, 2, 3, 4]
membership_roles = ('owner', 'manager', 'author', 'guest')

Expand Down
18 changes: 9 additions & 9 deletions rdmo/projects/tests/test_viewset_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@
)

view_project_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'manager': [1, 3, 5, 7],
'author': [1, 3, 5, 8],
'guest': [1, 3, 5, 9],
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

change_project_permission_map = {
'owner': [1, 2, 3, 4, 5],
'owner': [1, 2, 3, 4, 5, 10],
'manager': [1, 3, 5, 7],
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

delete_project_permission_map = {
'owner': [1, 2, 3, 4, 5],
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9]
'owner': [1, 2, 3, 4, 5, 10],
'api': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'site': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

urlnames = {
Expand Down
2 changes: 2 additions & 0 deletions testing/config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

PROJECT_SEND_INVITE = True

PROJECT_REMOVE_VIEWS = True

EMAIL_RECIPIENTS_CHOICES = [
('[email protected]', 'Emmi Email <[email protected]>'),
]
Expand Down
8 changes: 8 additions & 0 deletions testing/fixtures/groups.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,13 @@
"name": "api",
"permissions": []
}
},
{
"model": "auth.group",
"pk": 4,
"fields": {
"name": "view_test",
"permissions": []
}
}
]
49 changes: 49 additions & 0 deletions testing/fixtures/projects.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,24 @@
"status": "open"
}
},
{
"model": "projects.issue",
"pk": 6,
"fields": {
"project": 10,
"task": 2,
"status": "open"
}
},
{
"model": "projects.issue",
"pk": 7,
"fields": {
"project": 10,
"task": 1,
"status": "open"
}
},
{
"model": "projects.issueresource",
"pk": 1,
Expand Down Expand Up @@ -246,6 +264,15 @@
"role": "guest"
}
},
{
"model": "projects.membership",
"pk": 15,
"fields": {
"project": 10,
"user": 5,
"role": "owner"
}
},
{
"model": "projects.project",
"pk": 1,
Expand Down Expand Up @@ -426,6 +453,28 @@
]
}
},
{
"model": "projects.project",
"pk": 10,
"fields": {
"created": "2022-03-18T09:22:57.948Z",
"updated": "2022-03-18T09:26:17.893Z",
"parent": null,
"site": 1,
"title": "View Test",
"description": "",
"catalog": 1,
"lft": 1,
"rght": 2,
"tree_id": 7,
"level": 0,
"views": [
1,
2,
3
]
}
},
{
"model": "projects.snapshot",
"pk": 1,
Expand Down
4 changes: 3 additions & 1 deletion testing/fixtures/users.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@
"is_staff": false,
"is_active": true,
"date_joined": "2017-03-01T13:52:11Z",
"groups": [],
"groups": [
4
],
"user_permissions": []
}
},
Expand Down
Loading