Skip to content

Commit

Permalink
add contributor page improvements for Institutional Access
Browse files Browse the repository at this point in the history
  • Loading branch information
John Tordoff committed Dec 5, 2024
1 parent 869c146 commit e61b288
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 0 deletions.
61 changes: 61 additions & 0 deletions api_tests/nodes/views/test_node_contributor_insti_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import pytest
from osf.models import Contributor, NodeLog
from osf_tests.factories import (
AuthUserFactory,
ProjectFactory,
InstitutionFactory,
)
from api.base.settings.defaults import API_BASE
from rest_framework import status
from tests.utils import assert_latest_log


@pytest.mark.django_db
class TestChangeInstitutionalAdminContributor:

@pytest.fixture()
def user(self):
return AuthUserFactory()

@pytest.fixture()
def institution(self):
return InstitutionFactory()

@pytest.fixture()
def institutional_admin(self, institution):
admin_user = AuthUserFactory()
institution.get_group('institutional_admins').user_set.add(admin_user)
return admin_user

@pytest.fixture()
def project(self, user, institutional_admin):
project = ProjectFactory(creator=user)
project.add_contributor(institutional_admin, visible=False)
return project

@pytest.fixture()
def url_contrib(self, project, user):
return f'/{API_BASE}nodes/{project._id}/contributors/{user._id}/'

def test_cannot_set_institutional_admin_contributor_bibliographic(self, app, user, project, institutional_admin, url_contrib):
res = app.put_json_api(
url_contrib,
{
'data': {
'id': f'{project._id}-{institutional_admin._id}',
'type': 'contributors',
'attributes': {
'bibliographic': True,
}
}
},
auth=user.auth,
expect_errors=True
)
assert res.status_code == 409
project.reload()
contributor = Contributor.objects.get(
node=project,
user=institutional_admin
)
assert not contributor.visible
23 changes: 23 additions & 0 deletions osf/migrations/0025_contributor_institutional_admin_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.13 on 2024-12-04 23:25

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('osf', '0024_institution_link_to_external_reports_archive'),
]

operations = [
migrations.AddField(
model_name='contributor',
name='institutional_admin',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='osf.institution'),
),
migrations.AddConstraint(
model_name='contributor',
constraint=models.CheckConstraint(check=models.Q(('visible', True), ('institutional_admin__isnull', False), _negated=True), name='no_visible_with_institutional_admin'),
),
]
12 changes: 12 additions & 0 deletions osf/models/contributor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ def permission(self):

class Contributor(AbstractBaseContributor):
node = models.ForeignKey('AbstractNode', on_delete=models.CASCADE)
institutional_admin = models.ForeignKey(
'Institution',
on_delete=models.CASCADE,
null=True,
blank=True
)

@property
def _id(self):
Expand All @@ -40,6 +46,12 @@ class Meta:
# Make contributors orderable
# NOTE: Adds an _order column
order_with_respect_to = 'node'
constraints = [
models.CheckConstraint(
check=~(models.Q(visible=True) & models.Q(institutional_admin__isnull=False)),
name='no_visible_with_institutional_admin',
)
]


class PreprintContributor(AbstractBaseContributor):
Expand Down
75 changes: 75 additions & 0 deletions osf_tests/test_institutional_admin_contributors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import pytest
from django.core.exceptions import ValidationError
from osf.models import Contributor, Institution
from osf_tests.factories import AuthUserFactory, ProjectFactory, InstitutionFactory
from django.db.utils import IntegrityError

@pytest.mark.django_db
class TestContributorModel:

@pytest.fixture()
def user(self):
return AuthUserFactory()

@pytest.fixture()
def project(self, user):
return ProjectFactory(creator=user)

@pytest.fixture()
def institution(self):
return InstitutionFactory()

def test_contributor_with_visible_and_institutional_admin_raises_error(self, user, project, institution):
contributor = Contributor(
user=user,
node=project,
visible=True,
institutional_admin=institution
)
with pytest.raises(IntegrityError, match='new row for relation "osf_contributor" violates check constraint "no_visible_with_institutional_admin"'):
contributor.save() # Use clean() for validation logic

def test_contributor_with_visible_but_no_institutional_admin(self, user, project):
# Ensure no duplicate contributor exists
Contributor.objects.filter(user=user, node=project).delete()

contributor = Contributor(
user=user,
node=project,
visible=True,
institutional_admin=None
)
# This should not raise an error
contributor.save()

def test_contributor_with_institutional_admin_but_not_visible(self, user, project, institution):
# Ensure no duplicate contributor exists
Contributor.objects.filter(user=user, node=project).delete()

contributor = Contributor(
user=user,
node=project,
visible=False,
institutional_admin=institution
)
# This should not raise an error
contributor.save()

def test_database_constraint_no_visible_with_institutional_admin(self, user, project, institution):
# Ensure no duplicate contributor exists
Contributor.objects.filter(user=user, node=project).delete()

Contributor.objects.create(
user=user,
node=project,
visible=False,
institutional_admin=institution
) # Should succeed

with pytest.raises(Exception): # Check database constraint
Contributor.objects.create(
user=user,
node=project,
visible=True,
institutional_admin=institution
)
11 changes: 11 additions & 0 deletions website/templates/project/contributors.mako
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,16 @@
data-html="true"
></i>
</th>
<th class="curator-contrib">
Curator
<i class="fa fa-question-circle visibility-info"
data-toggle="popover"
data-title="Curator Information"
data-container="body"
data-placement="right"
data-html="true"
></i>
</th>
<th class="remove"></th>
</tr>
</thead>
Expand Down Expand Up @@ -315,6 +325,7 @@
/>
</div>
</td>
<td>Curator</td>
<td data-bind="css: {'add-remove': !$root.collapsed()}">
<div class="td-content" data-bind="visible: !$root.collapsed() || contributor.expanded()">
<!-- ko if: (contributor.canEdit() || canRemove) -->
Expand Down

0 comments on commit e61b288

Please sign in to comment.