Skip to content

Commit

Permalink
Show the list of accessors and uploaders on the detail view
Browse files Browse the repository at this point in the history
For a given CDSA, show the list of accessors (and uploaders if
applicable) on the Detail view for that signed agreement.
  • Loading branch information
amstilp committed Jun 19, 2024
1 parent 3ea744e commit 66fdd39
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 3 deletions.
144 changes: 144 additions & 0 deletions primed/cdsa/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from primed.duo.tests.factories import DataUseModifierFactory, DataUsePermissionFactory
from primed.miscellaneous_workspaces.tables import DataPrepWorkspaceUserTable
from primed.miscellaneous_workspaces.tests.factories import DataPrepWorkspaceFactory
from primed.primed_anvil.tables import UserAccountSingleGroupMembershipTable
from primed.primed_anvil.tests.factories import (
AvailableDataFactory,
StudyFactory,
Expand Down Expand Up @@ -2316,6 +2317,43 @@ def test_change_status_button_user_has_view_perm(self):
),
)

def test_table_classes(self):
"""Table classes are as expected."""
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertIn("table", response.context_data)
self.assertIsInstance(response.context_data["table"], UserAccountSingleGroupMembershipTable)

def test_accessor_table_none(self):
"""No accessors are shown if the signed agreement has no accessors."""
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 0)

def test_accessor_table_one(self):
"""One accessor is shown if the signed agreement has one accessors."""
accessor = UserFactory.create()
self.obj.signed_agreement.accessors.add(accessor)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 1)

def test_accessor_table_two(self):
"""Two accessors are shown if the signed agreement has two accessors."""
accessors = UserFactory.create_batch(2)
self.obj.signed_agreement.accessors.add(*accessors)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 2)

def test_accessor_table_only_from_this_application(self):
other_agreement = factories.MemberAgreementFactory.create()
other_accessor = UserFactory.create()
other_agreement.signed_agreement.accessors.add(other_accessor)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 0)

def test_response_is_primary(self):
"""Response includes info about requires_study_review."""
instance = factories.MemberAgreementFactory.create(
Expand Down Expand Up @@ -4094,6 +4132,75 @@ def test_response_requires_study_review(self):
html=True,
)

def test_table_classes(self):
"""Table classes are as expected."""
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertIn("tables", response.context_data)
self.assertEqual(len(response.context_data["tables"]), 2)
self.assertIsInstance(response.context_data["tables"][0], UserAccountSingleGroupMembershipTable)
self.assertIsInstance(response.context_data["tables"][1], UserAccountSingleGroupMembershipTable)

def test_accessor_table_none(self):
"""No accessors are shown if the signed agreement has no accessors."""
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][0].rows), 0)

def test_accessor_table_one(self):
"""One accessor is shown if the signed agreement has one accessors."""
accessor = UserFactory.create()
self.obj.signed_agreement.accessors.add(accessor)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][0].rows), 1)

def test_accessor_table_two(self):
"""Two accessors are shown if the signed agreement has two accessors."""
accessors = UserFactory.create_batch(2)
self.obj.signed_agreement.accessors.add(*accessors)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][0].rows), 2)

def test_accessor_table_only_from_this_application(self):
other_agreement = factories.DataAffiliateAgreementFactory.create()
other_accessor = UserFactory.create()
other_agreement.signed_agreement.accessors.add(other_accessor)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][0].rows), 0)

def test_uploader_table_none(self):
"""No uploaders are shown if the signed agreement has no uploaders."""
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][1].rows), 0)

def test_uploader_table_one(self):
"""One uploader is shown if the signed agreement has one uploader."""
uploader = UserFactory.create()
self.obj.uploaders.add(uploader)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][1].rows), 1)

def test_uploader_table_two(self):
"""Two uploaders are shown if the signed agreement has two uploaders."""
uploaders = UserFactory.create_batch(2)
self.obj.uploaders.add(*uploaders)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][1].rows), 2)

def test_uploader_table_only_from_this_application(self):
other_agreement = factories.DataAffiliateAgreementFactory.create()
other_uploader = UserFactory.create()
other_agreement.uploaders.add(other_uploader)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["tables"][1].rows), 0)


class DataAffiliateAgreementListTest(TestCase):
"""Tests for the DataAffiliateAgreement view."""
Expand Down Expand Up @@ -5104,6 +5211,43 @@ def test_change_status_button_user_has_view_perm(self):
),
)

def test_table_classes(self):
"""Table classes are as expected."""
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertIn("table", response.context_data)
self.assertIsInstance(response.context_data["table"], UserAccountSingleGroupMembershipTable)

def test_accessor_table_none(self):
"""No accessors are shown if the signed agreement has no accessors."""
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 0)

def test_accessor_table_one(self):
"""One accessor is shown if the signed agreement has one accessors."""
accessor = UserFactory.create()
self.obj.signed_agreement.accessors.add(accessor)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 1)

def test_accessor_table_two(self):
"""Two accessors are shown if the signed agreement has two accessors."""
accessors = UserFactory.create_batch(2)
self.obj.signed_agreement.accessors.add(*accessors)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 2)

def test_accessor_table_only_from_this_application(self):
other_agreement = factories.NonDataAffiliateAgreementFactory.create()
other_accessor = UserFactory.create()
other_agreement.signed_agreement.accessors.add(other_accessor)
self.client.force_login(self.user)
response = self.client.get(self.get_url(self.obj.signed_agreement.cc_id))
self.assertEqual(len(response.context_data["table"].rows), 0)


class NonDataAffiliateAgreementListTest(TestCase):
"""Tests for the NonDataAffiliateAgreement view."""
Expand Down
29 changes: 26 additions & 3 deletions primed/cdsa/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from django.views.generic.detail import SingleObjectMixin
from django_tables2 import MultiTableMixin, SingleTableMixin, SingleTableView

from primed.primed_anvil.tables import UserAccountSingleGroupMembershipTable

from . import forms, helpers, models, tables, viewmixins
from .audit import signed_agreement_audit, workspace_audit

Expand Down Expand Up @@ -347,11 +349,16 @@ def get_agreement_type(self, form, formset):
return agreement_type


class MemberAgreementDetail(viewmixins.SignedAgreementViewPermissionMixin, DetailView):
class MemberAgreementDetail(viewmixins.SignedAgreementViewPermissionMixin, SingleTableMixin, DetailView):
"""View to show details about a `MemberAgreement`."""

model = models.MemberAgreement

def get_table(self):
return UserAccountSingleGroupMembershipTable(
self.object.signed_agreement.accessors.all(), managed_group=self.object.signed_agreement.anvil_access_group
)

def get_object(self, queryset=None):
"""Look up the agreement by CDSA cc_id."""
queryset = self.get_queryset()
Expand Down Expand Up @@ -399,11 +406,22 @@ def get_object(self, queryset=None):
return obj


class DataAffiliateAgreementDetail(viewmixins.SignedAgreementViewPermissionMixin, DetailView):
class DataAffiliateAgreementDetail(viewmixins.SignedAgreementViewPermissionMixin, MultiTableMixin, DetailView):
"""View to show details about a `DataAffiliateAgreement`."""

model = models.DataAffiliateAgreement

def get_tables(self):
return (
UserAccountSingleGroupMembershipTable(
self.object.signed_agreement.accessors.all(),
managed_group=self.object.signed_agreement.anvil_access_group,
),
UserAccountSingleGroupMembershipTable(
self.object.uploaders.all(), managed_group=self.object.anvil_upload_group
),
)

def get_object(self, queryset=None):
"""Look up the agreement by CDSA cc_id."""
queryset = self.get_queryset()
Expand Down Expand Up @@ -449,11 +467,16 @@ class NonDataAffiliateAgreementCreate(
ERROR_CREATING_GROUP = "Error creating access group on AnVIL."


class NonDataAffiliateAgreementDetail(viewmixins.SignedAgreementViewPermissionMixin, DetailView):
class NonDataAffiliateAgreementDetail(viewmixins.SignedAgreementViewPermissionMixin, SingleTableMixin, DetailView):
"""View to show details about a `NonDataAffiliateAgreement`."""

model = models.NonDataAffiliateAgreement

def get_table(self):
return UserAccountSingleGroupMembershipTable(
self.object.signed_agreement.accessors.all(), managed_group=self.object.signed_agreement.anvil_access_group
)

def get_object(self, queryset=None):
"""Look up the agreement by CDSA cc_id."""
queryset = self.get_queryset()
Expand Down
56 changes: 56 additions & 0 deletions primed/templates/cdsa/dataaffiliateagreement_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,62 @@

{{ block.super }}

<div class="my-3">
<div class="accordion" id="accordionAccessors">
<div class="accordion-item">
<h2 class="accordion-header" id="headingAccessorsOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAccessorsOne" aria-expanded="falase" aria-controls="collapseAccessorsOne">
<span class="fa-solid fa-handshake mx-2"></span>
Named accessors
<span class="badge mx-2 bg-secondary pill"> {{ tables.0.rows|length }}</span>
</button>
</h2>
<div id="collapseAccessorsOne" class="accordion-collapse collapse" aria-labelledby="headingAccessorsOne" data-bs-parent="#accordionAccessors">
<div class="accordion-body">

<p>
This table shows accessors named for this signed agreement, their AnVIL account (if linked), and whether or not they are a member of the access group on AnVIL.
Only accessors who are PRIMED members are included.
An accessor must have linked their AnVIL account to be granted access to data on AnVIL.
If changes need to be made, please contact the CC.
</p>

{% render_table tables.0 %}

</div>
</div>
</div>
</div>
</div>

<div class="my-3">
<div class="accordion" id="accordionUploaders">
<div class="accordion-item">
<h2 class="accordion-header" id="headingUploadersOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseUploadersOne" aria-expanded="falase" aria-controls="collapseUploadersOne">
<span class="fa-solid fa-cloud-arrow-up mx-2"></span>
Named uploaders
<span class="badge mx-2 bg-secondary pill"> {{ tables.1.rows|length }}</span>
</button>
</h2>
<div id="collapseUploadersOne" class="accordion-collapse collapse" aria-labelledby="headingUploadersOne" data-bs-parent="#accordionUploaders">
<div class="accordion-body">

<p>
This table shows uploaders named for this signed agreement, their AnVIL account (if linked), and whether or not they are a member of the upload group on AnVIL.
Only uploaders who are PRIMED members are included.
An uploaders must have linked their AnVIL account to be granted access to data on AnVIL.
If changes need to be made, please contact the CC.
</p>

{% render_table tables.1 %}

</div>
</div>
</div>
</div>
</div>

{% endblock after_panel %}


Expand Down
29 changes: 29 additions & 0 deletions primed/templates/cdsa/memberagreement_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,35 @@
</dl>
{% endblock panel %}

{% block after_panel %}
<div class="my-3">
<div class="accordion" id="accordionAccessors">
<div class="accordion-item">
<h2 class="accordion-header" id="headingAccessorsOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAccessorsOne" aria-expanded="falase" aria-controls="collapseAccessorsOne">
<span class="fa-solid fa-handshake mx-2"></span>
Named accessors
<span class="badge mx-2 bg-secondary pill"> {{ table.rows|length }}</span>
</button>
</h2>
<div id="collapseAccessorsOne" class="accordion-collapse collapse" aria-labelledby="headingAccessorsOne" data-bs-parent="#accordionAccessors">
<div class="accordion-body">

<p>
This table shows accessors named for this signed agreement, their AnVIL account (if linked), and whether or not they are a member of the access group on AnVIL.
Only accessors who are PRIMED members are included.
An accessor must have linked their AnVIL account to be granted access to data on AnVIL.
If changes need to be made, please contact the CC.
</p>

{% render_table table %}

</div>
</div>
</div>
</div>
</div>
{% endblock after_panel %}

{% block action_buttons %}
{% if show_update_button %}
Expand Down
31 changes: 31 additions & 0 deletions primed/templates/cdsa/nondataaffiliateagreement_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,37 @@
{% endblock panel %}


{% block after_panel %}
<div class="my-3">
<div class="accordion" id="accordionAccessors">
<div class="accordion-item">
<h2 class="accordion-header" id="headingAccessorsOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAccessorsOne" aria-expanded="falase" aria-controls="collapseAccessorsOne">
<span class="fa-solid fa-handshake mx-2"></span>
Named accessors
<span class="badge mx-2 bg-secondary pill"> {{ table.rows|length }}</span>
</button>
</h2>
<div id="collapseAccessorsOne" class="accordion-collapse collapse" aria-labelledby="headingAccessorsOne" data-bs-parent="#accordionAccessors">
<div class="accordion-body">

<p>
This table shows accessors named for this signed agreement, their AnVIL account (if linked), and whether or not they are a member of the access group on AnVIL.
Only accessors who are PRIMED members are included.
An accessor must have linked their AnVIL account to be granted access to data on AnVIL.
If changes need to be made, please contact the CC.
</p>

{% render_table table %}

</div>
</div>
</div>
</div>
</div>
{% endblock after_panel %}


{% block action_buttons %}
{% if show_update_button %}
<a href="{% url 'cdsa:signed_agreements:non_data_affiliates:update' cc_id=object.signed_agreement.cc_id %}" class="btn btn-primary" role="button">Update status</a>
Expand Down

0 comments on commit 66fdd39

Please sign in to comment.