From 62af21311a09ff4967179f7bf2d53949ddb29f6c Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Fri, 3 Nov 2023 15:48:30 -0700 Subject: [PATCH 01/27] Add a publicly visible view for dbGaP records This view will have a link to the PRIMED coordinated applications that the CC has imported into dbGaP. It will be publicly viewable, similar to the CDSA records view. --- config/settings/base.py | 1 + primed/dbgap/tests/test_views.py | 33 +++++++++++++++++++++++ primed/dbgap/urls.py | 10 +++++++ primed/dbgap/views.py | 14 +++++++++- primed/templates/dbgap/records_index.html | 27 +++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 primed/templates/dbgap/records_index.html diff --git a/config/settings/base.py b/config/settings/base.py index 81499eeb..3c5c017a 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -308,6 +308,7 @@ "cdsa:records:studies", "cdsa:records:workspaces", "cdsa:records:user_access", + "dbgap:records:index", ] # django-dbbackup diff --git a/primed/dbgap/tests/test_views.py b/primed/dbgap/tests/test_views.py index 66ae2c8b..e592802d 100644 --- a/primed/dbgap/tests/test_views.py +++ b/primed/dbgap/tests/test_views.py @@ -4216,3 +4216,36 @@ def test_context_errors_table_no_dar_has_access(self): audit.dbGaPWorkspaceAccessAudit.ERROR_HAS_ACCESS, ) self.assertIsNotNone(table.rows[0].get_cell_value("action")) + + +class dbGaPRecordsIndexTest(TestCase): + """Tests for the dbGaPRecordsIndex view.""" + + def setUp(self): + """Set up test class.""" + self.factory = RequestFactory() + # Create a user with both view and edit permission. + self.user = User.objects.create_user(username="test", password="test") + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("dbgap:records:index", args=args) + + def test_status_code_not_logged_in(self): + "View redirects to login view when user is not logged in." + response = self.client.get(self.get_url()) + self.assertEqual(response.status_code, 200) + + def test_status_code_user_logged_in(self): + """Returns successful response code.""" + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertEqual(response.status_code, 200) + + def test_links(self): + """response includes the correct links.""" + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertContains(response, reverse("dbgap:records:applications")) + # self.assertContains(response, reverse("cdsa:records:user_access")) + # self.assertContains(response, reverse("cdsa:records:workspaces")) diff --git a/primed/dbgap/urls.py b/primed/dbgap/urls.py index 9b0395eb..a11897d7 100644 --- a/primed/dbgap/urls.py +++ b/primed/dbgap/urls.py @@ -76,8 +76,18 @@ ], "workspaces", ) + +records_patterns = ( + [ + path("", views.dbGaPRecordsIndex.as_view(), name="index"), + ], + "records", +) + + urlpatterns = [ path("studies/", include(dbgap_study_accession_patterns)), path("applications/", include(dbgap_application_patterns)), path("workspaces/", include(dbgap_workspace_patterns)), + path("records/", include(records_patterns)), ] diff --git a/primed/dbgap/views.py b/primed/dbgap/views.py index faffbbbf..7bb26340 100644 --- a/primed/dbgap/views.py +++ b/primed/dbgap/views.py @@ -22,7 +22,13 @@ from django.http import Http404 from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from django.views.generic import CreateView, DetailView, FormView, UpdateView +from django.views.generic import ( + CreateView, + DetailView, + FormView, + TemplateView, + UpdateView, +) from django_tables2 import SingleTableMixin, SingleTableView from django_tables2.export.views import ExportMixin @@ -570,3 +576,9 @@ def get_context_data(self, **kwargs): context["needs_action_table"] = data_access_audit.get_needs_action_table() context["data_access_audit"] = data_access_audit return context + + +class dbGaPRecordsIndex(TemplateView): + """Index page for dbGaP records.""" + + template_name = "dbgap/records_index.html" diff --git a/primed/templates/dbgap/records_index.html b/primed/templates/dbgap/records_index.html new file mode 100644 index 00000000..389b0c83 --- /dev/null +++ b/primed/templates/dbgap/records_index.html @@ -0,0 +1,27 @@ +{% extends "anvil_consortium_manager/base.html" %} + +{% block title %}dbGaP records{% endblock %} + +{% block content %} +

dbGaP records

+ + +
+ +
+
+

+
+
+

dbGaP coordinated applications

+

View dbGaP coordinated applications for PRIMED.

+ + View applications + +
+
+ +
+ + +{% endblock content %} From 3a2a6577ef446d9638a0134d9b63d8bfad1bc363 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Fri, 3 Nov 2023 16:08:50 -0700 Subject: [PATCH 02/27] Add a public facing dbGaP applications records page Add a table to display the records without links. Add a view, a template, a new url, and tests. --- config/settings/base.py | 1 + primed/dbgap/tables.py | 37 ++++++++++++++ primed/dbgap/tests/test_views.py | 51 +++++++++++++++++++ primed/dbgap/urls.py | 5 ++ primed/dbgap/views.py | 8 +++ .../dbgap/dbgapapplication_records.html | 15 ++++++ primed/templates/dbgap/records_index.html | 2 +- 7 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 primed/templates/dbgap/dbgapapplication_records.html diff --git a/config/settings/base.py b/config/settings/base.py index 3c5c017a..fa85385b 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -309,6 +309,7 @@ "cdsa:records:workspaces", "cdsa:records:user_access", "dbgap:records:index", + "dbgap:records:applications", ] # django-dbbackup diff --git a/primed/dbgap/tables.py b/primed/dbgap/tables.py index cfff4d04..2e075bcd 100644 --- a/primed/dbgap/tables.py +++ b/primed/dbgap/tables.py @@ -330,3 +330,40 @@ class Meta: model = models.dbGaPDataAccessRequest fields = ("dbgap_dac", "dbgap_current_status", "total") attrs = {"class": "table table-sm"} + + +class dbGaPApplicationRecordsTable(tables.Table): + """Class to render a publicly-viewable table of dbGaPApplication objects.""" + + dbgap_project_id = tables.columns.Column() + principal_investigator = tables.columns.Column( + verbose_name="Application PI", + accessor="principal_investigator__name", + ) + principal_investigator__study_sites = tables.columns.ManyToManyColumn( + verbose_name="Study site(s)", + ) + number_approved_dars = tables.columns.ManyToManyColumn( + accessor="dbgapdataaccesssnapshot_set", + filter=lambda qs: qs.filter(is_most_recent=True), + verbose_name="Number of approved DARs", + transform=lambda obj: obj.dbgapdataaccessrequest_set.approved().count(), + ) + number_requested_dars = tables.columns.ManyToManyColumn( + accessor="dbgapdataaccesssnapshot_set", + filter=lambda qs: qs.filter(is_most_recent=True), + verbose_name="Number of requested DARs", + transform=lambda obj: obj.dbgapdataaccessrequest_set.count(), + ) + last_update = ManyToManyDateTimeColumn( + accessor="dbgapdataaccesssnapshot_set", + filter=lambda qs: qs.filter(is_most_recent=True), + ) + + class Meta: + model = models.dbGaPApplication + fields = ( + "dbgap_project_id", + "principal_investigator", + ) + order_by = ("dbgap_project_id",) diff --git a/primed/dbgap/tests/test_views.py b/primed/dbgap/tests/test_views.py index e592802d..8020b2d2 100644 --- a/primed/dbgap/tests/test_views.py +++ b/primed/dbgap/tests/test_views.py @@ -4247,5 +4247,56 @@ def test_links(self): self.client.force_login(self.user) response = self.client.get(self.get_url()) self.assertContains(response, reverse("dbgap:records:applications")) + # In case we add similar public views later. # self.assertContains(response, reverse("cdsa:records:user_access")) # self.assertContains(response, reverse("cdsa:records:workspaces")) + + +class dbGaPApplicationRecordsListTest(TestCase): + """Tests for the dbGaPApplicationRecordsList view.""" + + def setUp(self): + """Set up test class.""" + self.factory = RequestFactory() + # Create a user with both view and edit permission. + self.user = User.objects.create_user(username="test", password="test") + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("dbgap:records:applications", args=args) + + def test_status_code_not_logged_in(self): + "View redirects to login view when user is not logged in." + # Need a client for redirects. + response = self.client.get(self.get_url()) + self.assertEqual(response.status_code, 200) + + def test_status_code_user_logged_in(self): + """Returns successful response code.""" + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertEqual(response.status_code, 200) + + def test_table_class(self): + """The table is the correct class.""" + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertIn("table", response.context_data) + self.assertIsInstance( + response.context_data["table"], tables.dbGaPApplicationRecordsTable + ) + + def test_table_no_rows(self): + """No rows are shown if there are no dbGaPApplications objects.""" + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertIn("table", response.context_data) + self.assertEqual(len(response.context_data["table"].rows), 0) + + def test_table_three_rows(self): + """Three rows are shown if there are three dbGaPApplication objects.""" + factories.dbGaPApplicationFactory.create_batch(3) + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertIn("table", response.context_data) + self.assertEqual(len(response.context_data["table"].rows), 3) diff --git a/primed/dbgap/urls.py b/primed/dbgap/urls.py index a11897d7..e49c633a 100644 --- a/primed/dbgap/urls.py +++ b/primed/dbgap/urls.py @@ -80,6 +80,11 @@ records_patterns = ( [ path("", views.dbGaPRecordsIndex.as_view(), name="index"), + path( + "applications/", + views.dbGaPApplicationRecords.as_view(), + name="applications", + ), ], "records", ) diff --git a/primed/dbgap/views.py b/primed/dbgap/views.py index 7bb26340..b01f4bec 100644 --- a/primed/dbgap/views.py +++ b/primed/dbgap/views.py @@ -582,3 +582,11 @@ class dbGaPRecordsIndex(TemplateView): """Index page for dbGaP records.""" template_name = "dbgap/records_index.html" + + +class dbGaPApplicationRecords(SingleTableView): + """Display a public list of dbGaP applications.""" + + model = models.dbGaPApplication + template_name = "dbgap/dbgapapplication_records.html" + table_class = tables.dbGaPApplicationRecordsTable diff --git a/primed/templates/dbgap/dbgapapplication_records.html b/primed/templates/dbgap/dbgapapplication_records.html new file mode 100644 index 00000000..89cd6019 --- /dev/null +++ b/primed/templates/dbgap/dbgapapplication_records.html @@ -0,0 +1,15 @@ +{% extends "anvil_consortium_manager/base.html" %} +{% load render_table from django_tables2 %} + +{% block title %}dbGaP application records{% endblock %} + +{% block content %} +

dbGaP application records

+ +
+

The following table shows the list of PRIMED coordinated dbGaP applications that the Coordinating Center is tracking as of {% now "SHORT_DATETIME_FORMAT" %}.

+
+ +{% render_table table %} + +{% endblock content %} diff --git a/primed/templates/dbgap/records_index.html b/primed/templates/dbgap/records_index.html index 389b0c83..23d72e8e 100644 --- a/primed/templates/dbgap/records_index.html +++ b/primed/templates/dbgap/records_index.html @@ -15,7 +15,7 @@

dbGaP coordinated applications

View dbGaP coordinated applications for PRIMED.

- + View applications
From bbced3b5e7f08dc5897fc6baa98fd5b4b164e285 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Tue, 7 Nov 2023 11:18:53 -0800 Subject: [PATCH 03/27] Add DataPrepWorkspace model Add the model, a migration, and tests. --- ...epworkspace_historicaldataprepworkspace.py | 56 +++++++++++++++ primed/miscellaneous_workspaces/models.py | 30 +++++++- .../tests/factories.py | 11 +++ .../tests/test_models.py | 72 +++++++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py diff --git a/primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py b/primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py new file mode 100644 index 00000000..642c6548 --- /dev/null +++ b/primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py @@ -0,0 +1,56 @@ +# Generated by Django 3.2.19 on 2023-11-07 18:52 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django_extensions.db.fields +import simple_history.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('anvil_consortium_manager', '0013_alter_anvilprojectmanageraccess_options'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('miscellaneous_workspaces', '0007_openaccessworkspace_data_url'), + ] + + operations = [ + migrations.CreateModel( + name='HistoricalDataPrepWorkspace', + fields=[ + ('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('requested_by', models.ForeignKey(blank=True, db_constraint=False, help_text='The user who requested creation.', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), + ('target_workspace', models.ForeignKey(blank=True, db_constraint=False, help_text='The workspace for which data is being prepared or updated.', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='anvil_consortium_manager.workspace')), + ('workspace', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='anvil_consortium_manager.workspace')), + ], + options={ + 'verbose_name': 'historical data prep workspace', + 'verbose_name_plural': 'historical data prep workspaces', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.CreateModel( + name='DataPrepWorkspace', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('requested_by', models.ForeignKey(help_text='The user who requested creation.', on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('target_workspace', models.ForeignKey(help_text='The workspace for which data is being prepared or updated.', on_delete=django.db.models.deletion.PROTECT, related_name='data_prep_workspaces', to='anvil_consortium_manager.workspace')), + ('workspace', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='anvil_consortium_manager.workspace')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/primed/miscellaneous_workspaces/models.py b/primed/miscellaneous_workspaces/models.py index f1f15b71..1ef65d19 100644 --- a/primed/miscellaneous_workspaces/models.py +++ b/primed/miscellaneous_workspaces/models.py @@ -1,7 +1,7 @@ """Model definitions for the `miscellaneous_workspaces` app.""" from anvil_consortium_manager.adapters.workspace import workspace_adapter_registry -from anvil_consortium_manager.models import BaseWorkspaceData +from anvil_consortium_manager.models import BaseWorkspaceData, Workspace from django.core.exceptions import ValidationError from django.db import models from django_extensions.db.models import TimeStampedModel @@ -62,3 +62,31 @@ class OpenAccessWorkspace(RequesterModel, TimeStampedModel, BaseWorkspaceData): help_text="The types of data available in this workspace.", blank=True, ) + + +class DataPrepWorkspace(RequesterModel, TimeStampedModel, BaseWorkspaceData): + """A model to track workspaces that are used to update data in another workspace.""" + + target_workspace = models.ForeignKey( + Workspace, + on_delete=models.PROTECT, + related_name="data_prep_workspaces", + help_text="The workspace for which data is being prepared or updated.", + ) + + def clean(self): + if self.target_workspace: + if self.target_workspace.workspace_type == "data_prep": + raise ValidationError( + { + "target_workspace": "target_workspace cannot be a DataPrepWorkspace." + } + ) + + if self.target_workspace and self.workspace: + if self.target_workspace == self.workspace: + raise ValidationError( + { + "target_workspace": "target_workspace must be different than workspace." + } + ) diff --git a/primed/miscellaneous_workspaces/tests/factories.py b/primed/miscellaneous_workspaces/tests/factories.py index db096983..a13699a8 100644 --- a/primed/miscellaneous_workspaces/tests/factories.py +++ b/primed/miscellaneous_workspaces/tests/factories.py @@ -76,3 +76,14 @@ class OpenAccessWorkspaceFactory(DjangoModelFactory): class Meta: model = models.OpenAccessWorkspace + + +class DataPrepWorkspaceFactory(DjangoModelFactory): + """A factory for the DataPrepWorkspace model.""" + + workspace = SubFactory(WorkspaceFactory, workspace_type="data_prep") + target_workspace = SubFactory(WorkspaceFactory) + requested_by = SubFactory(UserFactory) + + class Meta: + model = models.DataPrepWorkspace diff --git a/primed/miscellaneous_workspaces/tests/test_models.py b/primed/miscellaneous_workspaces/tests/test_models.py index a4d23a4b..661c89f0 100644 --- a/primed/miscellaneous_workspaces/tests/test_models.py +++ b/primed/miscellaneous_workspaces/tests/test_models.py @@ -223,3 +223,75 @@ def test_data_url(self): workspace=workspace, requested_by=user, data_url="http://www.example.com" ) self.assertEqual(instance.data_url, "http://www.example.com") + + +class DataPrepWorkspaceTest(TestCase): + """Tests for the DataPrepWorkspace model.""" + + def test_model_saving(self): + """Creation using the model constructor and .save() works.""" + workspace = WorkspaceFactory.create() + target_workspace = WorkspaceFactory.create() + user = UserFactory.create() + instance = models.DataPrepWorkspace( + workspace=workspace, target_workspace=target_workspace, requested_by=user + ) + instance.save() + self.assertIsInstance(instance, models.DataPrepWorkspace) + + def test_str_method(self): + workspace = WorkspaceFactory.create( + billing_project__name="test-bp", name="test-ws" + ) + instance = factories.DataPrepWorkspaceFactory.create(workspace=workspace) + self.assertIsInstance(str(instance), str) + self.assertEqual(str(instance), "test-bp/test-ws") + + def test_two_update_workspaces_for_same_final_workspace(self): + target_workspace = WorkspaceFactory.create() + instance_1 = factories.DataPrepWorkspaceFactory.create( + target_workspace=target_workspace + ) + instance_2 = factories.DataPrepWorkspaceFactory.create( + target_workspace=target_workspace + ) + self.assertEqual(target_workspace.data_prep_workspaces.count(), 2) + self.assertIn(instance_1, target_workspace.data_prep_workspaces.all()) + self.assertIn(instance_2, target_workspace.data_prep_workspaces.all()) + + def test_clean_original_workspace_different_than_workspace(self): + """Clean method raises ValidationError when workspace is the same as original_workspace.""" + workspace = WorkspaceFactory.create() + user = UserFactory.create() + instance = models.DataPrepWorkspace( + requested_by=user, workspace=workspace, target_workspace=workspace + ) + with self.assertRaises(ValidationError) as e: + instance.full_clean() + self.assertEqual(len(e.exception.message_dict), 1) + self.assertIn("target_workspace", e.exception.message_dict) + self.assertEqual(len(e.exception.message_dict["target_workspace"]), 1) + self.assertIn( + "target_workspace must be different", + e.exception.message_dict["target_workspace"][0], + ) + + def test_clean_target_workspace_cannot_be_a_data_prep_workspace(self): + """Clean method raises ValidationError when the original_workspace is a data prep workspace.""" + workspace = WorkspaceFactory.create() + target_workspace = factories.DataPrepWorkspaceFactory.create() + user = UserFactory.create() + instance = models.DataPrepWorkspace( + requested_by=user, + workspace=workspace, + target_workspace=target_workspace.workspace, + ) + with self.assertRaises(ValidationError) as e: + instance.full_clean() + self.assertEqual(len(e.exception.message_dict), 1) + self.assertIn("target_workspace", e.exception.message_dict) + self.assertEqual(len(e.exception.message_dict["target_workspace"]), 1) + self.assertIn( + "cannot be a DataPrepWorkspace", + e.exception.message_dict["target_workspace"][0], + ) From ac54d769b3effc4afb36345766d95eae5895eef5 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Tue, 7 Nov 2023 11:45:15 -0800 Subject: [PATCH 04/27] Add an adapter for DataPrepWorkspaces --- primed/miscellaneous_workspaces/adapters.py | 15 ++++ primed/miscellaneous_workspaces/forms.py | 23 +++++ primed/miscellaneous_workspaces/models.py | 4 +- primed/miscellaneous_workspaces/tables.py | 15 ++++ .../tests/test_forms.py | 84 +++++++++++++++++++ .../tests/test_tables.py | 21 +++++ 6 files changed, 160 insertions(+), 2 deletions(-) diff --git a/primed/miscellaneous_workspaces/adapters.py b/primed/miscellaneous_workspaces/adapters.py index 30d5d66b..4e207ed9 100644 --- a/primed/miscellaneous_workspaces/adapters.py +++ b/primed/miscellaneous_workspaces/adapters.py @@ -77,3 +77,18 @@ class OpenAccessWorkspaceAdapter(BaseWorkspaceAdapter): workspace_detail_template_name = ( "miscellaneous_workspaces/openaccessworkspace_detail.html" ) + + +class DataPrepWorkspaceAdapter(BaseWorkspaceAdapter): + """Adapter for DataPrepWorkspace.""" + + type = "data_prep" + name = "Data prep workspace" + description = "Workspaces used to prepare data." + list_table_class = tables.DataPrepWorkspaceTable + workspace_form_class = WorkspaceForm + workspace_data_model = models.DataPrepWorkspace + workspace_data_form_class = forms.DataPrepWorkspaceForm + workspace_detail_template_name = ( + "miscellaneous_workspaces/dataprpeworkspace_detail.html" + ) diff --git a/primed/miscellaneous_workspaces/forms.py b/primed/miscellaneous_workspaces/forms.py index 0aa1a1d3..58946b5b 100644 --- a/primed/miscellaneous_workspaces/forms.py +++ b/primed/miscellaneous_workspaces/forms.py @@ -106,3 +106,26 @@ class Meta: ), "available_data": forms.CheckboxSelectMultiple, } + + +class DataPrepWorkspaceForm(Bootstrap5MediaFormMixin, forms.ModelForm): + """Form for a DataPrepWorkspace object.""" + + class Meta: + model = models.DataPrepWorkspace + fields = ( + "workspace", + "target_workspace", + "requested_by", + ) + # widgets = { + # "studies": autocomplete.ModelSelect2Multiple( + # url="primed_anvil:studies:autocomplete", + # attrs={"data-theme": "bootstrap-5"}, + # ), + # "requested_by": autocomplete.ModelSelect2( + # url="users:autocomplete", + # attrs={"data-theme": "bootstrap-5"}, + # ), + # "available_data": forms.CheckboxSelectMultiple, + # } diff --git a/primed/miscellaneous_workspaces/models.py b/primed/miscellaneous_workspaces/models.py index 1ef65d19..40c8103c 100644 --- a/primed/miscellaneous_workspaces/models.py +++ b/primed/miscellaneous_workspaces/models.py @@ -75,7 +75,7 @@ class DataPrepWorkspace(RequesterModel, TimeStampedModel, BaseWorkspaceData): ) def clean(self): - if self.target_workspace: + if hasattr(self, "target_workspace"): if self.target_workspace.workspace_type == "data_prep": raise ValidationError( { @@ -83,7 +83,7 @@ def clean(self): } ) - if self.target_workspace and self.workspace: + if hasattr(self, "target_workspace") and hasattr(self, "workspace"): if self.target_workspace == self.workspace: raise ValidationError( { diff --git a/primed/miscellaneous_workspaces/tables.py b/primed/miscellaneous_workspaces/tables.py index 1a0a6c31..7f4feb52 100644 --- a/primed/miscellaneous_workspaces/tables.py +++ b/primed/miscellaneous_workspaces/tables.py @@ -40,3 +40,18 @@ class Meta: "is_shared", ) order_by = ("name",) + + +class DataPrepWorkspaceTable(tables.Table): + """Class to render a table of Workspace objects with DataPrepWorkspace workspace data.""" + + name = tables.columns.Column(linkify=True) + target_workspace = tables.columns.Column(linkify=True) + + class Meta: + model = Workspace + fields = ( + "name", + "target_workspace", + ) + order_by = ("name",) diff --git a/primed/miscellaneous_workspaces/tests/test_forms.py b/primed/miscellaneous_workspaces/tests/test_forms.py index 74f98a63..e2ff1f05 100644 --- a/primed/miscellaneous_workspaces/tests/test_forms.py +++ b/primed/miscellaneous_workspaces/tests/test_forms.py @@ -8,6 +8,7 @@ from primed.users.tests.factories import UserFactory from .. import forms +from . import factories class SimulatedDataWorkspaceFormTest(TestCase): @@ -379,3 +380,86 @@ def test_invalid_data_url_is_not_url(self): self.assertIn("data_url", form.errors) self.assertEqual(len(form.errors["data_url"]), 1) self.assertIn("valid URL", form.errors["data_url"][0]) + + +class DataPrepWorkspaceFormTest(TestCase): + + form_class = forms.DataPrepWorkspaceForm + + def setUp(self): + """Create a workspace for use in the form.""" + self.workspace = WorkspaceFactory.create() + # Use OpenAccessData workspaces for now. + self.target_workspace = factories.OpenAccessWorkspaceFactory.create().workspace + self.requester = UserFactory.create() + + def test_valid(self): + """Form is valid with necessary input.""" + form_data = { + "workspace": self.workspace, + "target_workspace": self.target_workspace, + "requested_by": self.requester, + } + form = self.form_class(data=form_data) + self.assertTrue(form.is_valid()) + + def test_invalid_missing_workspace(self): + """Form is invalid when missing workspace.""" + form_data = { + # "workspace": self.workspace, + "target_workspace": self.target_workspace, + "requested_by": self.requester, + } + form = self.form_class(data=form_data) + self.assertFalse(form.is_valid()) + self.assertEqual(len(form.errors), 1) + self.assertIn("workspace", form.errors) + self.assertEqual(len(form.errors["workspace"]), 1) + self.assertIn("required", form.errors["workspace"][0]) + + def test_invalid_missing_target_workspace(self): + """Form is invalid if target_workspace is missing.""" + form_data = { + "workspace": self.workspace, + # "target_workspace": self.target_workspace, + "requested_by": self.requester, + } + form = self.form_class(data=form_data) + self.assertFalse(form.is_valid()) + self.assertEqual(len(form.errors), 1) + self.assertIn("target_workspace", form.errors) + self.assertEqual(len(form.errors["target_workspace"]), 1) + self.assertIn("required", form.errors["target_workspace"][0]) + + def test_invalid_missing_requested_by(self): + """Form is invalid if requested_by is missing.""" + form_data = { + "workspace": self.workspace, + "target_workspace": self.target_workspace, + # "requested_by": self.requester + } + form = self.form_class(data=form_data) + self.assertFalse(form.is_valid()) + self.assertEqual(len(form.errors), 1) + self.assertIn("requested_by", form.errors) + self.assertEqual(len(form.errors["requested_by"]), 1) + self.assertIn("required", form.errors["requested_by"][0]) + + def test_form_all_registered_workspaces(self): + """Form is invalid if intended_workspace_type is not a registered type.""" + workspace_types = list(workspace_adapter_registry.get_registered_names().keys()) + for workspace_type in workspace_types: + if workspace_type == "data_prep": + # Cannot create data prep workspaces for data prep workspace target_workspaces. + pass + else: + target_workspace = WorkspaceFactory.create( + workspace_type=workspace_type + ) + form_data = { + "workspace": self.workspace, + "target_workspace": target_workspace, + "requested_by": self.requester, + } + form = self.form_class(data=form_data) + self.assertTrue(form.is_valid()) diff --git a/primed/miscellaneous_workspaces/tests/test_tables.py b/primed/miscellaneous_workspaces/tests/test_tables.py index dd36bc69..992ffcb3 100644 --- a/primed/miscellaneous_workspaces/tests/test_tables.py +++ b/primed/miscellaneous_workspaces/tests/test_tables.py @@ -47,3 +47,24 @@ def test_row_count_with_two_objects(self): self.model_factory.create_batch(2) table = self.table_class(Workspace.objects.all()) self.assertEqual(len(table.rows), 2) + + +class DataPrepWorkspaceTableTest(TestCase): + """Tests for the DataPrepWorkspaceTable table.""" + + model_factory = factories.DataPrepWorkspaceFactory + table_class = tables.DataPrepWorkspaceTable + + def test_row_count_with_no_objects(self): + table = self.table_class(Workspace.objects.filter(workspace_type="data_prep")) + self.assertEqual(len(table.rows), 0) + + def test_row_count_with_one_object(self): + self.model_factory.create() + table = self.table_class(Workspace.objects.filter(workspace_type="data_prep")) + self.assertEqual(len(table.rows), 1) + + def test_row_count_with_two_objects(self): + self.model_factory.create_batch(2) + table = self.table_class(Workspace.objects.filter(workspace_type="data_prep")) + self.assertEqual(len(table.rows), 2) From 2b134c7391b07196d2546e9ecf8c37232f14271a Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 11:36:03 -0800 Subject: [PATCH 05/27] Register the DataPrepWorkspaceAdapter Add tests of the WorkspaceCreate, WorkspaceImport, and WorkspaceDetail views. Note that the create and import views are broken due to a bug in ACM (which will be fixed in the next released version). Update the template show it shows a link back to the target workspace. --- config/settings/base.py | 1 + primed/miscellaneous_workspaces/adapters.py | 2 +- .../tests/test_views.py | 234 +++++++++++++++++- .../dataprepworkspace_detail.html | 13 + 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html diff --git a/config/settings/base.py b/config/settings/base.py index fa85385b..ed2cacd2 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -374,6 +374,7 @@ "primed.dbgap.adapters.dbGaPWorkspaceAdapter", "primed.cdsa.adapters.CDSAWorkspaceAdapter", "primed.miscellaneous_workspaces.adapters.ConsortiumDevelWorkspaceAdapter", + "primed.miscellaneous_workspaces.adapters.DataPrepWorkspaceAdapter", "primed.miscellaneous_workspaces.adapters.ExampleWorkspaceAdapter", "primed.miscellaneous_workspaces.adapters.SimulatedDataWorkspaceAdapter", "primed.miscellaneous_workspaces.adapters.OpenAccessWorkspaceAdapter", diff --git a/primed/miscellaneous_workspaces/adapters.py b/primed/miscellaneous_workspaces/adapters.py index 4e207ed9..feb17164 100644 --- a/primed/miscellaneous_workspaces/adapters.py +++ b/primed/miscellaneous_workspaces/adapters.py @@ -90,5 +90,5 @@ class DataPrepWorkspaceAdapter(BaseWorkspaceAdapter): workspace_data_model = models.DataPrepWorkspace workspace_data_form_class = forms.DataPrepWorkspaceForm workspace_detail_template_name = ( - "miscellaneous_workspaces/dataprpeworkspace_detail.html" + "miscellaneous_workspaces/dataprepworkspace_detail.html" ) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index 1272d501..a34a4214 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -2,7 +2,10 @@ import responses from anvil_consortium_manager.models import AnVILProjectManagerAccess, Workspace -from anvil_consortium_manager.tests.factories import BillingProjectFactory +from anvil_consortium_manager.tests.factories import ( + BillingProjectFactory, + WorkspaceFactory, +) from anvil_consortium_manager.tests.utils import AnVILAPIMockTestMixin from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission @@ -1149,3 +1152,232 @@ def test_creates_workspace(self): self.assertEqual(new_workspace_data.studies.count(), 1) self.assertIn(self.study, new_workspace_data.studies.all()) self.assertEqual(new_workspace_data.data_source, "test source") + + +class DataPrepWorkspaceDetailTest(TestCase): + """Tests of the WorkspaceDetail view from ACM with this app's DataPrepWorkspace model.""" + + def setUp(self): + """Set up test class.""" + # Create a user with both view and edit permission. + self.user = User.objects.create_user(username="test", password="test") + self.user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + ) + ) + + def test_status_code_with_user_permission(self): + """Returns successful response code.""" + obj = factories.DataPrepWorkspaceFactory.create() + self.client.force_login(self.user) + response = self.client.get(obj.workspace.get_absolute_url()) + self.assertEqual(response.status_code, 200) + # Includes link to target workspace. + self.assertContains(response, obj.target_workspace.get_absolute_url()) + + +class DataPrepWorkspaceCreateTest(AnVILAPIMockTestMixin, TestCase): + """Tests of the WorkspaceCreate view from ACM with this app's DataPrepWorkspace model.""" + + api_success_code = 201 + + def setUp(self): + """Set up test class.""" + # The superclass uses the responses package to mock API responses. + super().setUp() + # Create a user with both view and edit permissions. + self.user = User.objects.create_user(username="test", password="test") + self.user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + ) + ) + self.user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + ) + ) + self.requester = UserFactory.create() + self.target_workspace = WorkspaceFactory.create() + self.workspace_type = "simulated_data" + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("anvil_consortium_manager:workspaces:new", args=args) + + def get_api_url(self, billing_project_name, workspace_name): + """Return the Terra API url for a given billing project and workspace.""" + return ( + self.api_client.rawls_entry_point + + "/api/workspaces/" + + billing_project_name + + "/" + + workspace_name + ) + + def test_creates_workspace(self): + """Posting valid data to the form creates a workspace data object when using a custom adapter.""" + billing_project = BillingProjectFactory.create(name="test-billing-project") + url = self.api_client.rawls_entry_point + "/api/workspaces" + json_data = { + "namespace": "test-billing-project", + "name": "test-workspace", + "attributes": {}, + } + self.anvil_response_mock.add( + responses.POST, + url, + status=self.api_success_code, + match=[responses.matchers.json_params_matcher(json_data)], + ) + self.client.force_login(self.user) + response = self.client.post( + self.get_url(self.workspace_type), + { + "billing_project": billing_project.pk, + "name": "test-workspace", + # Workspace data form. + "workspacedata-TOTAL_FORMS": 1, + "workspacedata-INITIAL_FORMS": 0, + "workspacedata-MIN_NUM_FORMS": 1, + "workspacedata-MAX_NUM_FORMS": 1, + "workspacedata-0-target_workspace": self.target_workspace.pk, + "workspacedata-0-requested_by": self.requester.pk, + }, + ) + self.assertEqual(response.status_code, 302) + # The workspace is created. + new_workspace = Workspace.objects.latest("pk") + # Workspace data is added. + self.assertEqual(models.DataPrepWorkspaceFactory.objects.count(), 1) + new_workspace_data = models.DataPrepWorkspaceFactory.objects.latest("pk") + self.assertEqual(new_workspace_data.workspace, new_workspace) + + +class DataPrepWorkspaceImportTest(AnVILAPIMockTestMixin, TestCase): + """Tests of the WorkspaceImport view from ACM with this app's DataPrepWorkspace model.""" + + api_success_code = 200 + + def setUp(self): + """Set up test class.""" + # The superclass uses the responses package to mock API responses. + super().setUp() + # Create a user with both view and edit permissions. + self.user = User.objects.create_user(username="test", password="test") + self.user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + ) + ) + self.user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + ) + ) + self.requester = UserFactory.create() + self.target_workspace = WorkspaceFactory.create() + self.workspace_type = "data_prep" + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("anvil_consortium_manager:workspaces:import", args=args) + + def get_api_url(self, billing_project_name, workspace_name): + """Return the Terra API url for a given billing project and workspace.""" + return ( + self.api_client.rawls_entry_point + + "/api/workspaces/" + + billing_project_name + + "/" + + workspace_name + ) + + def get_api_json_response( + self, billing_project, workspace, authorization_domains=[], access="OWNER" + ): + """Return a pared down version of the json response from the AnVIL API with only fields we need.""" + json_data = { + "accessLevel": access, + "owners": [], + "workspace": { + "authorizationDomain": [ + {"membersGroupName": x} for x in authorization_domains + ], + "name": workspace, + "namespace": billing_project, + "isLocked": False, + }, + } + return json_data + + def test_creates_workspace(self): + """Posting valid data to the form creates an UploadWorkspace object.""" + billing_project = BillingProjectFactory.create(name="billing-project") + workspace_name = "workspace" + # Available workspaces API call. + workspace_list_url = self.api_client.rawls_entry_point + "/api/workspaces" + self.anvil_response_mock.add( + responses.GET, + workspace_list_url, + match=[ + responses.matchers.query_param_matcher( + {"fields": "workspace.namespace,workspace.name,accessLevel"} + ) + ], + status=200, + json=[self.get_api_json_response(billing_project.name, workspace_name)], + ) + url = self.get_api_url(billing_project.name, workspace_name) + self.anvil_response_mock.add( + responses.GET, + url, + status=self.api_success_code, + json=self.get_api_json_response(billing_project.name, workspace_name), + ) + # ACL API call. + api_url_acl = ( + self.api_client.rawls_entry_point + + "/api/workspaces/" + + billing_project.name + + "/" + + workspace_name + + "/acl" + ) + self.anvil_response_mock.add( + responses.GET, + api_url_acl, + status=200, + json={ + "acl": { + self.service_account_email: { + "accessLevel": "OWNER", + "canCompute": True, + "canShare": True, + "pending": False, + } + } + }, + ) + self.client.force_login(self.user) + response = self.client.post( + self.get_url(self.workspace_type), + { + "workspace": billing_project.name + "/" + workspace_name, + # Workspace data form. + "workspacedata-TOTAL_FORMS": 1, + "workspacedata-INITIAL_FORMS": 0, + "workspacedata-MIN_NUM_FORMS": 1, + "workspacedata-MAX_NUM_FORMS": 1, + "workspacedata-0-target_workspace": self.target_workspace.pk, + "workspacedata-0-requested_by": self.requester.pk, + }, + ) + self.assertEqual(response.status_code, 302) + # The workspace is created. + new_workspace = Workspace.objects.latest("pk") + # Workspace data is added. + self.assertEqual(models.DataPrepWorkspaceFactory.objects.count(), 1) + new_workspace_data = models.DataPrepWorkspaceFactory.objects.latest("pk") + self.assertEqual(new_workspace_data.workspace, new_workspace) diff --git a/primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html b/primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html new file mode 100644 index 00000000..f824351c --- /dev/null +++ b/primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html @@ -0,0 +1,13 @@ +{% extends "anvil_consortium_manager/workspace_detail.html" %} + +{% block workspace_data %} +
+
+
Target workspace
+ {{ object.dataprepworkspace.target_workspace }} +
+
Target type
+ {{ object.dataprepworkspace.target_workspace.workspace_type }} +
+
+{% endblock workspace_data %} From 9862deb4fd7fd7de46c113e2a4cdc6b6dfb1175a Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 11:47:02 -0800 Subject: [PATCH 06/27] Update ACM version in requirements --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index ef96e72c..70dafea2 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -22,7 +22,7 @@ django-dbbackup==4.0.1 # https://github.com/jazzband/django-dbbackup django-extensions==3.2.1 # https://github.com/django-extensions/django-extensions # anvil_consortium_manager -git+https://github.com/UW-GAC/django-anvil-consortium-manager.git@v0.19 +git+https://github.com/UW-GAC/django-anvil-consortium-manager.git@v0.20 # Simple history - model history tracking django-simple-history==3.1.1 From 89c30b108e73fcda169bf2694f31dbe4aed02c76 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 11:47:53 -0800 Subject: [PATCH 07/27] Update Auth mixin names for Edit -> StaffEdit --- primed/cdsa/views.py | 12 ++++++------ primed/dbgap/views.py | 12 ++++++------ primed/primed_anvil/views.py | 6 ++++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/primed/cdsa/views.py b/primed/cdsa/views.py index df0c4f8b..04006dfe 100644 --- a/primed/cdsa/views.py +++ b/primed/cdsa/views.py @@ -2,7 +2,7 @@ from anvil_consortium_manager.anvil_api import AnVILAPIError from anvil_consortium_manager.auth import ( - AnVILConsortiumManagerEditRequired, + AnVILConsortiumManagerStaffEditRequired, AnVILConsortiumManagerViewRequired, AnVILProjectManagerAccess, ) @@ -70,7 +70,7 @@ def get_context_data(self, **kwargs): class AgreementMajorVersionInvalidate( - AnVILConsortiumManagerEditRequired, SuccessMessageMixin, UpdateView + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, UpdateView ): """A view to invalidate an AgreementMajorVersion instance. @@ -294,7 +294,7 @@ def get_success_url(self): class MemberAgreementCreate( - AnVILConsortiumManagerEditRequired, + AnVILConsortiumManagerStaffEditRequired, AgreementTypeCreateMixin, SuccessMessageMixin, FormView, @@ -311,7 +311,7 @@ class MemberAgreementCreate( class DataAffiliateAgreementCreate( - AnVILConsortiumManagerEditRequired, + AnVILConsortiumManagerStaffEditRequired, AgreementTypeCreateMixin, SuccessMessageMixin, FormView, @@ -387,7 +387,7 @@ class MemberAgreementList(AnVILConsortiumManagerViewRequired, SingleTableView): class SignedAgreementStatusUpdate( - AnVILConsortiumManagerEditRequired, SuccessMessageMixin, UpdateView + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, UpdateView ): model = models.SignedAgreement @@ -450,7 +450,7 @@ class DataAffiliateAgreementList(AnVILConsortiumManagerViewRequired, SingleTable class NonDataAffiliateAgreementCreate( - AnVILConsortiumManagerEditRequired, + AnVILConsortiumManagerStaffEditRequired, AgreementTypeCreateMixin, SuccessMessageMixin, FormView, diff --git a/primed/dbgap/views.py b/primed/dbgap/views.py index b01f4bec..67c85f0a 100644 --- a/primed/dbgap/views.py +++ b/primed/dbgap/views.py @@ -3,7 +3,7 @@ import requests from anvil_consortium_manager.anvil_api import AnVILAPIError from anvil_consortium_manager.auth import ( - AnVILConsortiumManagerEditRequired, + AnVILConsortiumManagerStaffEditRequired, AnVILConsortiumManagerViewRequired, ) from anvil_consortium_manager.models import ( @@ -83,7 +83,7 @@ class dbGaPStudyAccessionList(AnVILConsortiumManagerViewRequired, SingleTableVie class dbGaPStudyAccessionCreate( - AnVILConsortiumManagerEditRequired, SuccessMessageMixin, CreateView + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, CreateView ): """View to create a new dbGaPStudyAccession.""" @@ -94,7 +94,7 @@ class dbGaPStudyAccessionCreate( class dbGaPStudyAccessionUpdate( - AnVILConsortiumManagerEditRequired, SuccessMessageMixin, UpdateView + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, UpdateView ): """View to update a dbGaPStudyAccession.""" @@ -193,7 +193,7 @@ class dbGaPApplicationList(AnVILConsortiumManagerViewRequired, SingleTableView): class dbGaPApplicationCreate( - AnVILConsortiumManagerEditRequired, SuccessMessageMixin, CreateView + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, CreateView ): """View to create a new dbGaPApplication.""" @@ -233,7 +233,7 @@ def form_valid(self, form): class dbGaPDataAccessSnapshotCreate( - AnVILConsortiumManagerEditRequired, SuccessMessageMixin, FormView + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, FormView ): form_class = forms.dbGaPDataAccessSnapshotForm @@ -321,7 +321,7 @@ def get_context_data(self, *args, **kwargs): class dbGaPDataAccessSnapshotCreateMultiple( - AnVILConsortiumManagerEditRequired, SuccessMessageMixin, FormView + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, FormView ): form_class = forms.dbGaPDataAccessSnapshotMultipleForm diff --git a/primed/primed_anvil/views.py b/primed/primed_anvil/views.py index 0d4b5cb3..6d0440ab 100644 --- a/primed/primed_anvil/views.py +++ b/primed/primed_anvil/views.py @@ -1,6 +1,6 @@ from anvil_consortium_manager.auth import ( - AnVILConsortiumManagerEditRequired, AnVILConsortiumManagerLimitedViewRequired, + AnVILConsortiumManagerStaffEditRequired, AnVILConsortiumManagerViewRequired, ) from anvil_consortium_manager.models import AnVILProjectManagerAccess, Workspace @@ -88,7 +88,9 @@ class StudyList(AnVILConsortiumManagerLimitedViewRequired, SingleTableView): table_class = tables.StudyTable -class StudyCreate(AnVILConsortiumManagerEditRequired, SuccessMessageMixin, CreateView): +class StudyCreate( + AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, CreateView +): """View to create a new `Study`.""" model = models.Study From c71e60ee94d12b81782726cf7b09d326259cdd07 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 11:48:22 -0800 Subject: [PATCH 08/27] Update Auth view mixin names for View -> StaffView --- primed/cdsa/views.py | 30 +++++++++++++++++------------- primed/dbgap/views.py | 22 ++++++++++++---------- primed/duo/views.py | 10 +++++----- primed/primed_anvil/views.py | 14 ++++++++------ 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/primed/cdsa/views.py b/primed/cdsa/views.py index 04006dfe..742dab35 100644 --- a/primed/cdsa/views.py +++ b/primed/cdsa/views.py @@ -3,7 +3,7 @@ from anvil_consortium_manager.anvil_api import AnVILAPIError from anvil_consortium_manager.auth import ( AnVILConsortiumManagerStaffEditRequired, - AnVILConsortiumManagerViewRequired, + AnVILConsortiumManagerStaffViewRequired, AnVILProjectManagerAccess, ) from anvil_consortium_manager.models import GroupAccountMembership, ManagedGroup @@ -26,7 +26,7 @@ class AgreementMajorVersionDetail( - AnVILConsortiumManagerViewRequired, MultiTableMixin, DetailView + AnVILConsortiumManagerStaffViewRequired, MultiTableMixin, DetailView ): """Display a "detail" page for an agreement major version (e.g., 1.x).""" @@ -132,7 +132,7 @@ def get_success_url(self): class AgreementVersionDetail( - AnVILConsortiumManagerViewRequired, SingleTableMixin, DetailView + AnVILConsortiumManagerStaffViewRequired, SingleTableMixin, DetailView ): """Display a "detail" page for an agreement major/minor version (e.g., 1.3).""" @@ -167,14 +167,14 @@ def get_context_data(self, **kwargs): return context -class AgreementVersionList(AnVILConsortiumManagerViewRequired, SingleTableView): +class AgreementVersionList(AnVILConsortiumManagerStaffViewRequired, SingleTableView): """Display a list of AgreementVersions.""" model = models.AgreementVersion table_class = tables.AgreementVersionTable -class SignedAgreementList(AnVILConsortiumManagerViewRequired, SingleTableView): +class SignedAgreementList(AnVILConsortiumManagerStaffViewRequired, SingleTableView): """Display a list of SignedAgreement objects.""" model = models.SignedAgreement @@ -348,7 +348,7 @@ def get_agreement_type(self, form, formset): return agreement_type -class MemberAgreementDetail(AnVILConsortiumManagerViewRequired, DetailView): +class MemberAgreementDetail(AnVILConsortiumManagerStaffViewRequired, DetailView): """View to show details about a `MemberAgreement`.""" model = models.MemberAgreement @@ -379,7 +379,7 @@ def get_context_data(self, **kwargs): return context -class MemberAgreementList(AnVILConsortiumManagerViewRequired, SingleTableView): +class MemberAgreementList(AnVILConsortiumManagerStaffViewRequired, SingleTableView): """Display a list of MemberAgreement objects.""" model = models.MemberAgreement @@ -411,7 +411,7 @@ def get_object(self, queryset=None): return obj -class DataAffiliateAgreementDetail(AnVILConsortiumManagerViewRequired, DetailView): +class DataAffiliateAgreementDetail(AnVILConsortiumManagerStaffViewRequired, DetailView): """View to show details about a `DataAffiliateAgreement`.""" model = models.DataAffiliateAgreement @@ -442,7 +442,9 @@ def get_context_data(self, **kwargs): return context -class DataAffiliateAgreementList(AnVILConsortiumManagerViewRequired, SingleTableView): +class DataAffiliateAgreementList( + AnVILConsortiumManagerStaffViewRequired, SingleTableView +): """Display a list of DataAffiliateAgreement objects.""" model = models.DataAffiliateAgreement @@ -466,7 +468,9 @@ class NonDataAffiliateAgreementCreate( ERROR_CREATING_GROUP = "Error creating access group on AnVIL." -class NonDataAffiliateAgreementDetail(AnVILConsortiumManagerViewRequired, DetailView): +class NonDataAffiliateAgreementDetail( + AnVILConsortiumManagerStaffViewRequired, DetailView +): """View to show details about a `NonDataAffiliateAgreement`.""" model = models.NonDataAffiliateAgreement @@ -498,7 +502,7 @@ def get_context_data(self, **kwargs): class NonDataAffiliateAgreementList( - AnVILConsortiumManagerViewRequired, SingleTableView + AnVILConsortiumManagerStaffViewRequired, SingleTableView ): """Display a list of NonDataAffiliateAgreement objects.""" @@ -506,7 +510,7 @@ class NonDataAffiliateAgreementList( table_class = tables.NonDataAffiliateAgreementTable -class SignedAgreementAudit(AnVILConsortiumManagerViewRequired, TemplateView): +class SignedAgreementAudit(AnVILConsortiumManagerStaffViewRequired, TemplateView): """View to show audit results for `SignedAgreements`.""" template_name = "cdsa/signedagreement_audit.html" @@ -538,7 +542,7 @@ def get_context_data(self, **kwargs): return context -class CDSAWorkspaceAudit(AnVILConsortiumManagerViewRequired, TemplateView): +class CDSAWorkspaceAudit(AnVILConsortiumManagerStaffViewRequired, TemplateView): """View to show audit results for `CDSAWorkspaces`.""" template_name = "cdsa/cdsaworkspace_audit.html" diff --git a/primed/dbgap/views.py b/primed/dbgap/views.py index 67c85f0a..4685a795 100644 --- a/primed/dbgap/views.py +++ b/primed/dbgap/views.py @@ -4,7 +4,7 @@ from anvil_consortium_manager.anvil_api import AnVILAPIError from anvil_consortium_manager.auth import ( AnVILConsortiumManagerStaffEditRequired, - AnVILConsortiumManagerViewRequired, + AnVILConsortiumManagerStaffViewRequired, ) from anvil_consortium_manager.models import ( AnVILProjectManagerAccess, @@ -38,7 +38,7 @@ class dbGaPStudyAccessionDetail( - AnVILConsortiumManagerViewRequired, SingleTableMixin, DetailView + AnVILConsortiumManagerStaffViewRequired, SingleTableMixin, DetailView ): """View to show details about a `dbGaPStudyAccession`.""" @@ -75,7 +75,7 @@ def get_context_data(self, **kwargs): return context -class dbGaPStudyAccessionList(AnVILConsortiumManagerViewRequired, SingleTableView): +class dbGaPStudyAccessionList(AnVILConsortiumManagerStaffViewRequired, SingleTableView): """View to show a list of dbGaPStudyAccession objects.""" model = models.dbGaPStudyAccession @@ -116,7 +116,7 @@ def get_object(self, queryset=None): class dbGaPStudyAccessionAutocomplete( - AnVILConsortiumManagerViewRequired, autocomplete.Select2QuerySetView + AnVILConsortiumManagerStaffViewRequired, autocomplete.Select2QuerySetView ): """View to provide autocompletion for dbGaPStudyAccessions.""" @@ -135,7 +135,7 @@ def get_queryset(self): class dbGaPApplicationDetail( - AnVILConsortiumManagerViewRequired, SingleTableMixin, DetailView + AnVILConsortiumManagerStaffViewRequired, SingleTableMixin, DetailView ): """View to show details about a `dbGaPApplication`.""" @@ -185,7 +185,7 @@ def get_context_data(self, *args, **kwargs): return context -class dbGaPApplicationList(AnVILConsortiumManagerViewRequired, SingleTableView): +class dbGaPApplicationList(AnVILConsortiumManagerStaffViewRequired, SingleTableView): """View to show a list of dbGaPApplication objects.""" model = models.dbGaPApplication @@ -431,7 +431,9 @@ def form_valid(self, form): return super().form_valid(form) -class dbGaPDataAccessSnapshotDetail(AnVILConsortiumManagerViewRequired, DetailView): +class dbGaPDataAccessSnapshotDetail( + AnVILConsortiumManagerStaffViewRequired, DetailView +): """View to show details about a `dbGaPDataAccessSnapshot`.""" model = models.dbGaPDataAccessSnapshot @@ -478,7 +480,7 @@ def get_context_data(self, **kwargs): class dbGaPDataAccessRequestList( - AnVILConsortiumManagerViewRequired, ExportMixin, SingleTableView + AnVILConsortiumManagerStaffViewRequired, ExportMixin, SingleTableView ): """View to show current DARs.""" @@ -492,7 +494,7 @@ def get_table_data(self): ) -class dbGaPApplicationAudit(AnVILConsortiumManagerViewRequired, DetailView): +class dbGaPApplicationAudit(AnVILConsortiumManagerStaffViewRequired, DetailView): """View to show audit results for a `dbGaPApplication`.""" model = models.dbGaPApplication @@ -536,7 +538,7 @@ def get_context_data(self, **kwargs): return context -class dbGaPWorkspaceAudit(AnVILConsortiumManagerViewRequired, DetailView): +class dbGaPWorkspaceAudit(AnVILConsortiumManagerStaffViewRequired, DetailView): """View to show audit results for a `dbGaPWorkspace`.""" model = models.dbGaPWorkspace diff --git a/primed/duo/views.py b/primed/duo/views.py index 308cc3e1..06c7c749 100644 --- a/primed/duo/views.py +++ b/primed/duo/views.py @@ -1,11 +1,11 @@ -from anvil_consortium_manager.auth import AnVILConsortiumManagerViewRequired +from anvil_consortium_manager.auth import AnVILConsortiumManagerStaffViewRequired from django.http import Http404 from django.views.generic import DetailView, ListView from . import models -class DataUsePermissionList(AnVILConsortiumManagerViewRequired, ListView): +class DataUsePermissionList(AnVILConsortiumManagerStaffViewRequired, ListView): model = models.DataUsePermission @@ -18,7 +18,7 @@ def get_context_data(self, **kwargs): return context -class DataUsePermissionDetail(AnVILConsortiumManagerViewRequired, DetailView): +class DataUsePermissionDetail(AnVILConsortiumManagerStaffViewRequired, DetailView): model = models.DataUsePermission @@ -38,7 +38,7 @@ def get_context_data(self, **kwargs): return context -class DataUseModifierList(AnVILConsortiumManagerViewRequired, ListView): +class DataUseModifierList(AnVILConsortiumManagerStaffViewRequired, ListView): model = models.DataUseModifier @@ -51,7 +51,7 @@ def get_context_data(self, **kwargs): return context -class DataUseModifierDetail(AnVILConsortiumManagerViewRequired, DetailView): +class DataUseModifierDetail(AnVILConsortiumManagerStaffViewRequired, DetailView): model = models.DataUseModifier diff --git a/primed/primed_anvil/views.py b/primed/primed_anvil/views.py index 6d0440ab..3146033f 100644 --- a/primed/primed_anvil/views.py +++ b/primed/primed_anvil/views.py @@ -1,7 +1,7 @@ from anvil_consortium_manager.auth import ( AnVILConsortiumManagerLimitedViewRequired, AnVILConsortiumManagerStaffEditRequired, - AnVILConsortiumManagerViewRequired, + AnVILConsortiumManagerStaffViewRequired, ) from anvil_consortium_manager.models import AnVILProjectManagerAccess, Workspace from dal import autocomplete @@ -102,7 +102,7 @@ def get_success_url(self): class StudyAutocomplete( - AnVILConsortiumManagerViewRequired, autocomplete.Select2QuerySetView + AnVILConsortiumManagerStaffViewRequired, autocomplete.Select2QuerySetView ): """View to provide autocompletion for `Study`s. Match either the `short_name` or `full_name`.""" @@ -126,7 +126,9 @@ def get_queryset(self): return qs -class StudySiteDetail(AnVILConsortiumManagerViewRequired, MultiTableMixin, DetailView): +class StudySiteDetail( + AnVILConsortiumManagerStaffViewRequired, MultiTableMixin, DetailView +): """View to show details about a `StudySite`.""" model = models.StudySite @@ -147,14 +149,14 @@ def get_tables_data(self): return [user_qs, dbgap_qs, cdsa_qs] -class StudySiteList(AnVILConsortiumManagerViewRequired, SingleTableView): +class StudySiteList(AnVILConsortiumManagerStaffViewRequired, SingleTableView): """View to show a list of `StudySite`s.""" model = models.StudySite table_class = tables.StudySiteTable -class AvailableDataList(AnVILConsortiumManagerViewRequired, SingleTableView): +class AvailableDataList(AnVILConsortiumManagerStaffViewRequired, SingleTableView): """View to show a list of `AvailableData`.""" model = models.AvailableData @@ -162,7 +164,7 @@ class AvailableDataList(AnVILConsortiumManagerViewRequired, SingleTableView): class AvailableDataDetail( - AnVILConsortiumManagerViewRequired, SingleTableMixin, DetailView + AnVILConsortiumManagerStaffViewRequired, SingleTableMixin, DetailView ): """View to show details about a `AvailableData`.""" From ff1c51c3480a35b10fcf168d2c147ba034f2ad41 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 11:50:25 -0800 Subject: [PATCH 09/27] Update Auth mixin names for LimitedView -> View --- primed/primed_anvil/views.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/primed/primed_anvil/views.py b/primed/primed_anvil/views.py index 3146033f..9f6f6ff9 100644 --- a/primed/primed_anvil/views.py +++ b/primed/primed_anvil/views.py @@ -1,7 +1,7 @@ from anvil_consortium_manager.auth import ( - AnVILConsortiumManagerLimitedViewRequired, AnVILConsortiumManagerStaffEditRequired, AnVILConsortiumManagerStaffViewRequired, + AnVILConsortiumManagerViewRequired, ) from anvil_consortium_manager.models import AnVILProjectManagerAccess, Workspace from dal import autocomplete @@ -37,9 +37,7 @@ User = get_user_model() -class StudyDetail( - AnVILConsortiumManagerLimitedViewRequired, MultiTableMixin, DetailView -): +class StudyDetail(AnVILConsortiumManagerViewRequired, MultiTableMixin, DetailView): """View to show details about a `Study`.""" model = models.Study @@ -81,7 +79,7 @@ def get_tables(self): ) -class StudyList(AnVILConsortiumManagerLimitedViewRequired, SingleTableView): +class StudyList(AnVILConsortiumManagerViewRequired, SingleTableView): """View to show a list of `Study`s.""" model = models.Study From 1d5136914c4939389ec57a9055700adf00dd1b15 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 12:48:11 -0800 Subject: [PATCH 10/27] Update auth attribute references for EDIT -> STAFF_EDIT --- primed/cdsa/tests/test_views.py | 26 +++++++++---------- primed/cdsa/views.py | 8 +++--- primed/dbgap/tests/test_views.py | 16 ++++++------ primed/dbgap/views.py | 4 ++- .../tests/test_views.py | 20 +++++++------- primed/primed_anvil/tests/test_views.py | 2 +- 6 files changed, 39 insertions(+), 37 deletions(-) diff --git a/primed/cdsa/tests/test_views.py b/primed/cdsa/tests/test_views.py index 1689582b..70923600 100644 --- a/primed/cdsa/tests/test_views.py +++ b/primed/cdsa/tests/test_views.py @@ -264,7 +264,7 @@ def test_invalidate_button_valid_user_has_edit_perm(self): ) user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.client.force_login(user) @@ -301,7 +301,7 @@ def test_invalidate_button_invalid_user_has_edit_perm(self): ) user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.client.force_login(user) @@ -345,7 +345,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -836,7 +836,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -996,7 +996,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -1156,7 +1156,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -1316,7 +1316,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -2263,7 +2263,7 @@ def test_change_status_button_user_has_edit_perm(self): ) user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.client.force_login(user) @@ -2383,7 +2383,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -3481,7 +3481,7 @@ def test_change_status_button_user_has_edit_perm(self): ) user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.client.force_login(user) @@ -3601,7 +3601,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -4526,7 +4526,7 @@ def test_change_status_button_user_has_edit_perm(self): ) user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.client.force_login(user) @@ -5545,7 +5545,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() diff --git a/primed/cdsa/views.py b/primed/cdsa/views.py index 742dab35..952804a7 100644 --- a/primed/cdsa/views.py +++ b/primed/cdsa/views.py @@ -59,7 +59,7 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["show_deprecation_message"] = not self.object.is_valid edit_permission_codename = "anvil_consortium_manager." + ( - AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) context[ "show_invalidate_button" @@ -371,7 +371,7 @@ def get_context_data(self, **kwargs): "show_deprecation_message" ] = not self.object.signed_agreement.version.major_version.is_valid edit_permission_codename = "anvil_consortium_manager." + ( - AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) context["show_update_button"] = self.request.user.has_perm( edit_permission_codename @@ -434,7 +434,7 @@ def get_context_data(self, **kwargs): "show_deprecation_message" ] = not self.object.signed_agreement.version.major_version.is_valid edit_permission_codename = "anvil_consortium_manager." + ( - AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) context["show_update_button"] = self.request.user.has_perm( edit_permission_codename @@ -493,7 +493,7 @@ def get_context_data(self, **kwargs): "show_deprecation_message" ] = not self.object.signed_agreement.version.major_version.is_valid edit_permission_codename = "anvil_consortium_manager." + ( - AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) context["show_update_button"] = self.request.user.has_perm( edit_permission_codename diff --git a/primed/dbgap/tests/test_views.py b/primed/dbgap/tests/test_views.py index 8020b2d2..99b77436 100644 --- a/primed/dbgap/tests/test_views.py +++ b/primed/dbgap/tests/test_views.py @@ -261,7 +261,7 @@ def test_context_show_edit_links_with_edit_permission(self): codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME ), Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ), ) self.client.force_login(edit_user) @@ -314,7 +314,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -511,7 +511,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -876,7 +876,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -1026,7 +1026,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -1493,7 +1493,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) @@ -1829,7 +1829,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.dbgap_application = factories.dbGaPApplicationFactory.create() @@ -2555,7 +2555,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) diff --git a/primed/dbgap/views.py b/primed/dbgap/views.py index 4685a795..545830b4 100644 --- a/primed/dbgap/views.py +++ b/primed/dbgap/views.py @@ -68,7 +68,9 @@ def get_table(self): def get_context_data(self, **kwargs): """Add show_edit_links to context data.""" context = super().get_context_data(**kwargs) - edit_permission_codename = AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + edit_permission_codename = ( + AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME + ) context["show_edit_links"] = self.request.user.has_perm( "anvil_consortium_manager." + edit_permission_codename ) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index 1272d501..2db63d55 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -57,7 +57,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -133,7 +133,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -280,7 +280,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -356,7 +356,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -503,7 +503,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -579,7 +579,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -726,7 +726,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.workspace_type = "template" @@ -802,7 +802,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.workspace_type = "template" @@ -953,7 +953,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.workspace_type = "open_access" @@ -1036,7 +1036,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.workspace_type = "open_access" diff --git a/primed/primed_anvil/tests/test_views.py b/primed/primed_anvil/tests/test_views.py index 42066720..1942a1d5 100644 --- a/primed/primed_anvil/tests/test_views.py +++ b/primed/primed_anvil/tests/test_views.py @@ -460,7 +460,7 @@ def setUp(self): ) self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) From ea1aa5d8155ade6284d4a0b8fac1c4dbe35e74ad Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 12:51:00 -0800 Subject: [PATCH 11/27] Update auth attribute references for VIEW -> STAFF VIEW --- primed/cdsa/tests/test_views.py | 66 +++++++++---------- primed/dbgap/tests/test_views.py | 50 +++++++------- primed/duo/tests/test_views.py | 8 +-- .../tests/test_views.py | 30 ++++----- primed/primed_anvil/tests/test_views.py | 20 +++--- primed/primed_anvil/views.py | 2 +- primed/users/tests/test_views.py | 4 +- 7 files changed, 90 insertions(+), 90 deletions(-) diff --git a/primed/cdsa/tests/test_views.py b/primed/cdsa/tests/test_views.py index 70923600..a34a3405 100644 --- a/primed/cdsa/tests/test_views.py +++ b/primed/cdsa/tests/test_views.py @@ -50,7 +50,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -122,7 +122,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create an object test this with. @@ -259,7 +259,7 @@ def test_invalidate_button_valid_user_has_edit_perm(self): user = User.objects.create_user(username="test_edit", password="test_edit") user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) user.user_permissions.add( @@ -296,7 +296,7 @@ def test_invalidate_button_invalid_user_has_edit_perm(self): user = User.objects.create_user(username="test_edit", password="test_edit") user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) user.user_permissions.add( @@ -340,7 +340,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -390,7 +390,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url(instance.version)) @@ -558,7 +558,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create an object test this with. @@ -756,7 +756,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -831,7 +831,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -870,7 +870,7 @@ def test_access_with_view_permission(self): ) user_with_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url(1)) @@ -991,7 +991,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1030,7 +1030,7 @@ def test_access_with_view_permission(self): ) user_with_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url(1)) @@ -1151,7 +1151,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1190,7 +1190,7 @@ def test_access_with_view_permission(self): ) user_with_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url(1)) @@ -1311,7 +1311,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1359,7 +1359,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url()) @@ -2158,7 +2158,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create an object test this with. @@ -2258,7 +2258,7 @@ def test_change_status_button_user_has_edit_perm(self): user = User.objects.create_user(username="test_edit", password="test_edit") user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) user.user_permissions.add( @@ -2305,7 +2305,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -2378,7 +2378,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -2426,7 +2426,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url()) @@ -3370,7 +3370,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create an object test this with. @@ -3476,7 +3476,7 @@ def test_change_status_button_user_has_edit_perm(self): user = User.objects.create_user(username="test_edit", password="test_edit") user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) user.user_permissions.add( @@ -3523,7 +3523,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -3596,7 +3596,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -3644,7 +3644,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url()) @@ -4427,7 +4427,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create an object test this with. @@ -4521,7 +4521,7 @@ def test_change_status_button_user_has_edit_perm(self): user = User.objects.create_user(username="test_edit", password="test_edit") user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) user.user_permissions.add( @@ -4568,7 +4568,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -4744,7 +4744,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create the test group. @@ -4923,7 +4923,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.anvil_cdsa_group = ManagedGroupFactory.create(name="TEST_PRIMED_CDSA") @@ -5471,7 +5471,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -5540,7 +5540,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( diff --git a/primed/dbgap/tests/test_views.py b/primed/dbgap/tests/test_views.py index 99b77436..775e54d4 100644 --- a/primed/dbgap/tests/test_views.py +++ b/primed/dbgap/tests/test_views.py @@ -67,7 +67,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -150,7 +150,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create an object test this with. @@ -258,7 +258,7 @@ def test_context_show_edit_links_with_edit_permission(self): edit_user = User.objects.create_user(username="edit", password="test") edit_user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ), Permission.objects.get( codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME @@ -281,7 +281,7 @@ def test_context_show_edit_links_with_view_permission(self): view_user = User.objects.create_user(username="edit", password="test") view_user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ), ) self.client.force_login(view_user) @@ -309,7 +309,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -358,7 +358,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url()) @@ -506,7 +506,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -545,7 +545,7 @@ def test_access_with_view_permission(self): ) user_with_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url(instance.dbgap_phs)) @@ -645,7 +645,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -782,7 +782,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.workspace_type = "dbgap" @@ -815,7 +815,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -871,7 +871,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1021,7 +1021,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1250,7 +1250,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -1307,7 +1307,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) # Create an object test this with. @@ -1488,7 +1488,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1537,7 +1537,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url()) @@ -1824,7 +1824,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1891,7 +1891,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get( @@ -2550,7 +2550,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -2606,7 +2606,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url()) @@ -3097,7 +3097,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.application = factories.dbGaPApplicationFactory.create() @@ -3416,7 +3416,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -3513,7 +3513,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.application = factories.dbGaPApplicationFactory.create() @@ -3777,7 +3777,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.dbgap_workspace = factories.dbGaPWorkspaceFactory.create() diff --git a/primed/duo/tests/test_views.py b/primed/duo/tests/test_views.py index 8988271f..5ebd2bc6 100644 --- a/primed/duo/tests/test_views.py +++ b/primed/duo/tests/test_views.py @@ -23,7 +23,7 @@ def setUp(self): self.user = UserFactory.create(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -126,7 +126,7 @@ def setUp(self): self.user = UserFactory.create(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -180,7 +180,7 @@ def setUp(self): self.user = UserFactory.create(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -282,7 +282,7 @@ def setUp(self): self.user = UserFactory.create(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index 2db63d55..f4041a88 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -27,7 +27,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -52,7 +52,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -128,7 +128,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -250,7 +250,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -275,7 +275,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -351,7 +351,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -473,7 +473,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -498,7 +498,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -574,7 +574,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -696,7 +696,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -721,7 +721,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -797,7 +797,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -919,7 +919,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -948,7 +948,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -1031,7 +1031,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( diff --git a/primed/primed_anvil/tests/test_views.py b/primed/primed_anvil/tests/test_views.py index 1942a1d5..db8abd65 100644 --- a/primed/primed_anvil/tests/test_views.py +++ b/primed/primed_anvil/tests/test_views.py @@ -51,7 +51,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -98,7 +98,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -251,7 +251,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -455,7 +455,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( @@ -504,7 +504,7 @@ def test_access_without_user_permission_view(self): ) user_view_perm.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) request = self.factory.get(self.get_url()) @@ -633,7 +633,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -739,7 +739,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -844,7 +844,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -938,7 +938,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -995,7 +995,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) diff --git a/primed/primed_anvil/views.py b/primed/primed_anvil/views.py index 9f6f6ff9..b4fdb98b 100644 --- a/primed/primed_anvil/views.py +++ b/primed/primed_anvil/views.py @@ -61,7 +61,7 @@ def get_tables(self): ) # Check permissions to determine table type. apm_content_type = ContentType.objects.get_for_model(AnVILProjectManagerAccess) - full_view_perm = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME}" + full_view_perm = f"{apm_content_type.app_label}.{AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME}" if self.request.user.has_perm(full_view_perm): return ( dbGaPWorkspaceTable(dbgap_qs), diff --git a/primed/users/tests/test_views.py b/primed/users/tests/test_views.py index 4dfea800..7151fc19 100644 --- a/primed/users/tests/test_views.py +++ b/primed/users/tests/test_views.py @@ -119,7 +119,7 @@ def test_authenticated_with_verified_account( client.force_login(user) user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) AccountFactory.create(email="foo@bar.com", user=user, verified=True) @@ -134,7 +134,7 @@ def test_authenticated_with_user_email_entry( client.force_login(user) user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) UserEmailEntryFactory.create(email="foo@bar.com", user=user) From 8407d7af4a6242d512866d3afa0ff784771ecde5 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 12:51:40 -0800 Subject: [PATCH 12/27] Update auth attribute references for LIMITED_VIEW -> VIEW --- primed/primed_anvil/tests/test_views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/primed/primed_anvil/tests/test_views.py b/primed/primed_anvil/tests/test_views.py index db8abd65..48b544c6 100644 --- a/primed/primed_anvil/tests/test_views.py +++ b/primed/primed_anvil/tests/test_views.py @@ -150,7 +150,7 @@ def test_status_code_with_limited_view_permission(self): user = User.objects.create_user(username="test-2", password="test-2") user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.LIMITED_VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME ) ) self.client.force_login(user) @@ -173,7 +173,7 @@ def test_table_classes_limited_view_permission(self): user = User.objects.create_user(username="test-2", password="test-2") user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.LIMITED_VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME ) ) obj = self.model_factory.create() @@ -673,7 +673,7 @@ def test_status_code_with_limited_view_permission(self): user = User.objects.create_user(username="test-2", password="test-2") user.user_permissions.add( Permission.objects.get( - codename=acm_models.AnVILProjectManagerAccess.LIMITED_VIEW_PERMISSION_CODENAME + codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME ) ) self.client.force_login(user) From 7e279ca5c32c0eaaee813ffbda89ca464cfe225f Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 15:57:21 -0800 Subject: [PATCH 13/27] Fix permissions in user_detail template Use the new permissions in the user_detail template (view -> staff_view). Add a test to check that the link to the Account page is only shown for users with staff_view permission. --- primed/templates/users/user_detail.html | 2 +- primed/users/tests/test_views.py | 26 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/primed/templates/users/user_detail.html b/primed/templates/users/user_detail.html index d80f6b94..c58c5171 100644 --- a/primed/templates/users/user_detail.html +++ b/primed/templates/users/user_detail.html @@ -59,7 +59,7 @@

{% if object == request.user %}My{% else %}
  • Account Email
    - {% if perms.anvil_consortium_manager.anvil_project_manager_view %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_view %} {{ object.account.email }} {% else %} {{ object.account.email }} diff --git a/primed/users/tests/test_views.py b/primed/users/tests/test_views.py index 7151fc19..8fcced81 100644 --- a/primed/users/tests/test_views.py +++ b/primed/users/tests/test_views.py @@ -174,6 +174,32 @@ def test_not_authenticated(self, user: User, rf: RequestFactory): assert response.status_code == 302 assert response["Location"] == f"{login_url}?next=/fake-url/" + def test_staff_view_links(self, client, user: User, rf: RequestFactory): + """Link to ACM account page is in response for users with STAFF_VIEW permission.""" + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + client.force_login(user) + account = AccountFactory.create(email="foo@bar.com", user=user, verified=True) + user_detail_url = reverse("users:detail", kwargs=dict(username=user.username)) + response = client.get(user_detail_url) + assert account.get_absolute_url() in str(response.content) + + def test_view_links(self, client, user: User, rf: RequestFactory): + """Link to ACM account page is not in response for users with VIEW permission.""" + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + ) + ) + client.force_login(user) + account = AccountFactory.create(email="foo@bar.com", user=user, verified=True) + user_detail_url = reverse("users:detail", kwargs=dict(username=user.username)) + response = client.get(user_detail_url) + assert account.get_absolute_url() not in str(response.content) + class UserAutocompleteTest(TestCase): def setUp(self): From aebffd1ee57a1f75c95673a281950f1c0fed38da Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 16:02:10 -0800 Subject: [PATCH 14/27] Fix permissions in the study_detail template Use the new view -> staff_view permission in the study_detail template. Add a tests to check that the relevant information is displayed iff the user has staff_view permission. --- primed/primed_anvil/tests/test_views.py | 18 ++++++++++++++++++ .../templates/primed_anvil/study_detail.html | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/primed/primed_anvil/tests/test_views.py b/primed/primed_anvil/tests/test_views.py index 48b544c6..e52b1bbf 100644 --- a/primed/primed_anvil/tests/test_views.py +++ b/primed/primed_anvil/tests/test_views.py @@ -157,6 +157,24 @@ def test_status_code_with_limited_view_permission(self): response = self.client.get(self.get_url(obj.pk)) self.assertEqual(response.status_code, 200) + def test_content_staff_view_permission(self): + obj = self.model_factory.create() + self.client.force_login(self.user) + response = self.client.get(self.get_url(obj.pk)) + self.assertContains(response, "Date created") + + def test_content_view_permission(self): + obj = self.model_factory.create() + user = User.objects.create_user(username="test-2", password="test-2") + user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url(obj.pk)) + self.assertNotContains(response, "Date created") + def test_table_classes_view_permission(self): obj = self.model_factory.create() self.client.force_login(self.user) diff --git a/primed/templates/primed_anvil/study_detail.html b/primed/templates/primed_anvil/study_detail.html index 313fcdf7..998f2f33 100644 --- a/primed/templates/primed_anvil/study_detail.html +++ b/primed/templates/primed_anvil/study_detail.html @@ -8,7 +8,7 @@
    Short name
    {{ object.short_name }}
    Full name
    {{ object.full_name }}
    - {% if perms.anvil_consortium_manager.anvil_project_manager_view %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_view %}
    Date created
    {{ object.created }}
    Date modified
    {{ object.modified }}
    {% endif %} From 07e40e2cada5bb3cd45fea3bb4d10bb5995bdb58 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 16:05:21 -0800 Subject: [PATCH 15/27] Fix permissions in dbGaPApplicationDetail template Change the permissions from view -> staff_view. Add tests to check that the information is only displayed when the user has the expected permissions. --- primed/dbgap/tests/test_views.py | 37 +++++++++++++++++++ .../dbgap/dbgapapplication_detail.html | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/primed/dbgap/tests/test_views.py b/primed/dbgap/tests/test_views.py index 775e54d4..f6d0517f 100644 --- a/primed/dbgap/tests/test_views.py +++ b/primed/dbgap/tests/test_views.py @@ -1363,6 +1363,43 @@ def test_view_status_code_with_invalid_pk(self): with self.assertRaises(Http404): self.get_view()(request, pk=self.obj.dbgap_project_id + 1) + def test_staff_edit_links(self): + self.user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME + ) + ) + self.client.force_login(self.user) + response = self.client.get(self.get_url(self.obj.dbgap_project_id)) + self.assertContains( + response, + reverse( + "dbgap:dbgap_applications:dbgap_data_access_snapshots:new", + args=[self.obj.dbgap_project_id], + ), + ) + self.assertContains( + response, + reverse("dbgap:dbgap_applications:audit", args=[self.obj.dbgap_project_id]), + ) + "dbgap:dbgap_applications:dbgap_data_access_snapshots:new" + + def test_staff_view_links(self): + """No edit links if staff user only has view permission.""" + self.client.force_login(self.user) + response = self.client.get(self.get_url(self.obj.dbgap_project_id)) + self.assertNotContains( + response, + reverse( + "dbgap:dbgap_applications:dbgap_data_access_snapshots:new", + args=[self.obj.dbgap_project_id], + ), + ) + self.assertContains( + response, + reverse("dbgap:dbgap_applications:audit", args=[self.obj.dbgap_project_id]), + ) + def test_context_snapshot_table(self): """The data_access_snapshot_table exists in the context.""" request = self.factory.get(self.get_url(self.obj.dbgap_project_id)) diff --git a/primed/templates/dbgap/dbgapapplication_detail.html b/primed/templates/dbgap/dbgapapplication_detail.html index 7fe61e84..d1fa63d6 100644 --- a/primed/templates/dbgap/dbgapapplication_detail.html +++ b/primed/templates/dbgap/dbgapapplication_detail.html @@ -51,7 +51,7 @@

    {% block action_buttons %}

    - {% if perms.anvil_consortium_manager.anvil_project_manager_edit %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %} Update data access requests {% endif %} Audit workspace access From d52609531a7e02ab29f7b198539d5f95d66d2331 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 16:10:01 -0800 Subject: [PATCH 16/27] Fix dbGaP navbar items for new permissions Update the permissions in the navitems template for the dbGaP app to use the new permissions in ACM v0.20. Add tests to check that the relevant links are only displayed when appropriate. --- primed/dbgap/tests/test_views.py | 54 +++++++++++++++++++++++++++ primed/templates/dbgap/nav_items.html | 4 +- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/primed/dbgap/tests/test_views.py b/primed/dbgap/tests/test_views.py index f6d0517f..1791ca41 100644 --- a/primed/dbgap/tests/test_views.py +++ b/primed/dbgap/tests/test_views.py @@ -57,6 +57,60 @@ def tearDown(self): self.dbgap_response_mock.reset() +class NavbarTest(TestCase): + """Tests for the navbar involving CDSA links.""" + + def setUp(self): + """Set up test class.""" + self.factory = RequestFactory() + # Create a user with both view and edit permission. + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("anvil_consortium_manager:index", args=args) + + def test_links_for_staff_view(self): + """Returns successful response code.""" + user = User.objects.create_user(username="test", password="test") + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + self.assertContains(response, reverse("dbgap:dbgap_study_accessions:list")) + self.assertContains(response, reverse("dbgap:dbgap_applications:list")) + # Links to add dbGaP info. + self.assertNotContains(response, reverse("dbgap:dbgap_study_accessions:new")) + self.assertNotContains(response, reverse("dbgap:dbgap_applications:new")) + self.assertNotContains( + response, reverse("dbgap:dbgap_applications:update_dars") + ) + + def test_links_for_staff_edit(self): + """Returns successful response code.""" + user = User.objects.create_user(username="test", password="test") + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + self.assertContains(response, reverse("dbgap:dbgap_study_accessions:list")) + self.assertContains(response, reverse("dbgap:dbgap_applications:list")) + # Links to add dbGaP info. + self.assertContains(response, reverse("dbgap:dbgap_study_accessions:new")) + self.assertContains(response, reverse("dbgap:dbgap_applications:new")) + self.assertContains(response, reverse("dbgap:dbgap_applications:update_dars")) + + class dbGaPStudyAccessionListTest(TestCase): """Tests for the dbGaPStudyAccessionList view.""" diff --git a/primed/templates/dbgap/nav_items.html b/primed/templates/dbgap/nav_items.html index 64d9e009..b35bf5c1 100644 --- a/primed/templates/dbgap/nav_items.html +++ b/primed/templates/dbgap/nav_items.html @@ -6,7 +6,7 @@

  • List dbGaP study accessions
  • - {% if perms.anvil_consortium_manager.anvil_project_manager_edit %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
  • Add a new dbGaP study accession
  • @@ -17,7 +17,7 @@
  • List dbGaP applications
  • - {% if perms.anvil_consortium_manager.anvil_project_manager_edit %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
  • Add a new dbGaP application
  • From 3e8d5bb419a0924338c1faaff849d150f8c2c7b2 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 16:12:19 -0800 Subject: [PATCH 17/27] Update CDSA nav items template for new permissions Use the new ACM v0.20 permissions in the CDSA nav items template. Add tests to check that information is only displayed when the user has appropriate permissions. --- primed/cdsa/tests/test_views.py | 64 ++++++++++++++++++++++++++++ primed/templates/cdsa/nav_items.html | 2 +- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/primed/cdsa/tests/test_views.py b/primed/cdsa/tests/test_views.py index a34a3405..86deb9d8 100644 --- a/primed/cdsa/tests/test_views.py +++ b/primed/cdsa/tests/test_views.py @@ -40,6 +40,70 @@ User = get_user_model() +class NavbarTest(TestCase): + """Tests for the navbar involving CDSA links.""" + + def setUp(self): + """Set up test class.""" + self.factory = RequestFactory() + # Create a user with both view and edit permission. + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("anvil_consortium_manager:index", args=args) + + def test_links_for_staff_view(self): + """Returns successful response code.""" + user = User.objects.create_user(username="test", password="test") + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + self.assertContains(response, reverse("cdsa:agreement_versions:list")) + self.assertContains(response, reverse("cdsa:audit:signed_agreements")) + self.assertContains(response, reverse("cdsa:audit:workspaces")) + self.assertContains(response, reverse("cdsa:records:index")) + # Links to add CDSAs. + self.assertNotContains(response, reverse("cdsa:signed_agreements:members:new")) + self.assertNotContains( + response, reverse("cdsa:signed_agreements:data_affiliates:new") + ) + self.assertNotContains( + response, reverse("cdsa:signed_agreements:non_data_affiliates:new") + ) + + def test_links_for_staff_edit(self): + """Returns successful response code.""" + user = User.objects.create_user(username="test", password="test") + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + user.user_permissions.add( + Permission.objects.get( + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + self.assertContains(response, reverse("cdsa:agreement_versions:list")) + self.assertContains(response, reverse("cdsa:audit:signed_agreements")) + self.assertContains(response, reverse("cdsa:audit:workspaces")) + self.assertContains(response, reverse("cdsa:records:index")) + # Links to add CDSAs. + self.assertContains(response, reverse("cdsa:signed_agreements:members:new")) + self.assertContains( + response, reverse("cdsa:signed_agreements:data_affiliates:new") + ) + self.assertContains( + response, reverse("cdsa:signed_agreements:non_data_affiliates:new") + ) + + class AgreementVersionListTest(TestCase): """Tests for the AgreementVersionList view.""" diff --git a/primed/templates/cdsa/nav_items.html b/primed/templates/cdsa/nav_items.html index 13e26408..04081cc1 100644 --- a/primed/templates/cdsa/nav_items.html +++ b/primed/templates/cdsa/nav_items.html @@ -25,7 +25,7 @@
  • - {% if perms.anvil_consortium_manager.anvil_project_manager_edit %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
  • Member From a7ed5d2ba15b5db71641ff56d7c714f7158a826e Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 16:21:24 -0800 Subject: [PATCH 18/27] Fix permissions in base template Update the view -> staff_view permissions in the base template. Add tests to ensure that the link to the ACM index page only appears for users with staff_view permission. --- primed/primed_anvil/tests/test_views.py | 30 +++++++++++++++++++++++++ primed/templates/base.html | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/primed/primed_anvil/tests/test_views.py b/primed/primed_anvil/tests/test_views.py index e52b1bbf..5e23a40a 100644 --- a/primed/primed_anvil/tests/test_views.py +++ b/primed/primed_anvil/tests/test_views.py @@ -86,6 +86,36 @@ def test_user_has_not_linked_account(self): response, reverse("anvil_consortium_manager:accounts:link") ) + def test_staff_view_links(self): + user = UserFactory.create() + user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + # Note: we need quotes around the link because anvil/accounts/link does appear in the response, + # so we can't test if "anvil/" is in the response. We need to test if '"anvil/"' is in the response. + self.assertContains( + response, '"{}"'.format(reverse("anvil_consortium_manager:index")) + ) + + def test_view_links(self): + user = UserFactory.create() + user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + # Note: we need quotes around the link because anvil/accounts/link does appear in the response, + # so we can't test if "anvil/" is in the response. We need to test if '"anvil/"' is in the response. + self.assertNotContains( + response, '"{}"'.format(reverse("anvil_consortium_manager:index")) + ) + class StudyDetailTest(TestCase): """Tests for the StudyDetail view.""" diff --git a/primed/templates/base.html b/primed/templates/base.html index 9a696dd6..c6b0a9aa 100644 --- a/primed/templates/base.html +++ b/primed/templates/base.html @@ -96,7 +96,7 @@
  • - {% if perms.anvil_consortium_manager.anvil_project_manager_view %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_view %} From d04de1e1b906188473ec9323470f81a9c6332ed3 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Wed, 8 Nov 2023 16:26:28 -0800 Subject: [PATCH 19/27] Fix permissions in overridden ACM navbar template Use the new edit -> staff_edit permission in the overridden ACM navbar template. Add tests to check that links only appear when appropriate. --- primed/primed_anvil/tests/test_views.py | 47 +++++++++++++++++++ .../anvil_consortium_manager/navbar.html | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/primed/primed_anvil/tests/test_views.py b/primed/primed_anvil/tests/test_views.py index 5e23a40a..85d59706 100644 --- a/primed/primed_anvil/tests/test_views.py +++ b/primed/primed_anvil/tests/test_views.py @@ -40,6 +40,53 @@ User = get_user_model() +class ACMNavbarTest(TestCase): + """Tests for the ACM navbar.""" + + def setUp(self): + """Set up test class.""" + self.factory = RequestFactory() + self.model_factory = factories.StudyFactory + # Create a user with both view and edit permission. + self.user = User.objects.create_user(username="test", password="test") + self.user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("anvil_consortium_manager:index", args=args) + + def test_staff_view_links(self): + user = UserFactory.create() + user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + self.assertNotContains(response, reverse("primed_anvil:studies:new")) + + def test_staff_edit_links(self): + user = UserFactory.create() + user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME + ) + ) + self.client.force_login(user) + response = self.client.get(self.get_url()) + self.assertContains(response, reverse("primed_anvil:studies:new")) + + class HomeTest(TestCase): """Tests of the home page. This is maybe not the best place to put this test?""" diff --git a/primed/templates/anvil_consortium_manager/navbar.html b/primed/templates/anvil_consortium_manager/navbar.html index 27f8ac29..6dc96662 100644 --- a/primed/templates/anvil_consortium_manager/navbar.html +++ b/primed/templates/anvil_consortium_manager/navbar.html @@ -15,7 +15,7 @@
  • List studies
  • - {% if perms.anvil_consortium_manager.anvil_project_manager_edit %} + {% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
  • Add a new study
  • From 70ab22590f10bd8a65771a7dc3097a4a79a07acf Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 09:25:56 -0800 Subject: [PATCH 20/27] Fix permission names after merging with main In the tests, fix the permission codenames after merging with main. The main branch is updated to ACM v0.20, which fixes a bug found in this branch but also includes updated permissions. --- primed/miscellaneous_workspaces/tests/test_views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index 19d00fac..652cf866 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -1163,7 +1163,7 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) @@ -1190,12 +1190,12 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() @@ -1268,12 +1268,12 @@ def setUp(self): self.user = User.objects.create_user(username="test", password="test") self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME ) ) self.user.user_permissions.add( Permission.objects.get( - codename=AnVILProjectManagerAccess.EDIT_PERMISSION_CODENAME + codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME ) ) self.requester = UserFactory.create() From 42fd8313a0b1ac35f16f40fed40cd2bafc95d536 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 09:27:39 -0800 Subject: [PATCH 21/27] Misc test cleanup after ACM bugfix Fix failures that arose after the ACM bugfix was installed. --- primed/miscellaneous_workspaces/tests/test_views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index 652cf866..20f506b4 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -1200,7 +1200,7 @@ def setUp(self): ) self.requester = UserFactory.create() self.target_workspace = WorkspaceFactory.create() - self.workspace_type = "simulated_data" + self.workspace_type = "data_prep" def get_url(self, *args): """Get the url for the view being tested.""" @@ -1250,8 +1250,8 @@ def test_creates_workspace(self): # The workspace is created. new_workspace = Workspace.objects.latest("pk") # Workspace data is added. - self.assertEqual(models.DataPrepWorkspaceFactory.objects.count(), 1) - new_workspace_data = models.DataPrepWorkspaceFactory.objects.latest("pk") + self.assertEqual(models.DataPrepWorkspace.objects.count(), 1) + new_workspace_data = models.DataPrepWorkspace.objects.latest("pk") self.assertEqual(new_workspace_data.workspace, new_workspace) @@ -1378,6 +1378,6 @@ def test_creates_workspace(self): # The workspace is created. new_workspace = Workspace.objects.latest("pk") # Workspace data is added. - self.assertEqual(models.DataPrepWorkspaceFactory.objects.count(), 1) - new_workspace_data = models.DataPrepWorkspaceFactory.objects.latest("pk") + self.assertEqual(models.DataPrepWorkspace.objects.count(), 1) + new_workspace_data = models.DataPrepWorkspace.objects.latest("pk") self.assertEqual(new_workspace_data.workspace, new_workspace) From a30002314606e5e1b8df855542c6616c95179558 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 09:44:45 -0800 Subject: [PATCH 22/27] Remove unused test case methods The tests for creating different workspace types using the ACM WorkspaceCreate view had unused helper methods to return an api url. Remove these method definitions since they are not being used anywhere in the tests. --- .../tests/test_views.py | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index 20f506b4..b8ac42ed 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -293,16 +293,6 @@ def get_url(self, *args): """Get the url for the view being tested.""" return reverse("anvil_consortium_manager:workspaces:new", args=args) - def get_api_url(self, billing_project_name, workspace_name): - """Return the Terra API url for a given billing project and workspace.""" - return ( - self.api_client.rawls_entry_point - + "/api/workspaces/" - + billing_project_name - + "/" - + workspace_name - ) - def test_creates_workspace(self): """Posting valid data to the form creates a workspace data object when using a custom adapter.""" billing_project = BillingProjectFactory.create(name="test-billing-project") @@ -516,16 +506,6 @@ def get_url(self, *args): """Get the url for the view being tested.""" return reverse("anvil_consortium_manager:workspaces:new", args=args) - def get_api_url(self, billing_project_name, workspace_name): - """Return the Terra API url for a given billing project and workspace.""" - return ( - self.api_client.rawls_entry_point - + "/api/workspaces/" - + billing_project_name - + "/" - + workspace_name - ) - def test_creates_workspace(self): """Posting valid data to the form creates a workspace data object when using a custom adapter.""" billing_project = BillingProjectFactory.create(name="test-billing-project") @@ -738,16 +718,6 @@ def get_url(self, *args): """Get the url for the view being tested.""" return reverse("anvil_consortium_manager:workspaces:new", args=args) - def get_api_url(self, billing_project_name, workspace_name): - """Return the Terra API url for a given billing project and workspace.""" - return ( - self.api_client.rawls_entry_point - + "/api/workspaces/" - + billing_project_name - + "/" - + workspace_name - ) - def test_creates_workspace(self): """Posting valid data to the form creates a workspace data object when using a custom adapter.""" billing_project = BillingProjectFactory.create(name="test-billing-project") @@ -967,16 +937,6 @@ def get_url(self, *args): """Get the url for the view being tested.""" return reverse("anvil_consortium_manager:workspaces:new", args=args) - def get_api_url(self, billing_project_name, workspace_name): - """Return the Terra API url for a given billing project and workspace.""" - return ( - self.api_client.rawls_entry_point - + "/api/workspaces/" - + billing_project_name - + "/" - + workspace_name - ) - def test_creates_workspace(self): """Posting valid data to the form creates a workspace data object when using a custom adapter.""" billing_project = BillingProjectFactory.create(name="test-billing-project") @@ -1206,16 +1166,6 @@ def get_url(self, *args): """Get the url for the view being tested.""" return reverse("anvil_consortium_manager:workspaces:new", args=args) - def get_api_url(self, billing_project_name, workspace_name): - """Return the Terra API url for a given billing project and workspace.""" - return ( - self.api_client.rawls_entry_point - + "/api/workspaces/" - + billing_project_name - + "/" - + workspace_name - ) - def test_creates_workspace(self): """Posting valid data to the form creates a workspace data object when using a custom adapter.""" billing_project = BillingProjectFactory.create(name="test-billing-project") From af0caae7ef2c0a4f79b1b4b0de7019257d63622e Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 10:44:34 -0800 Subject: [PATCH 23/27] Forgot to remove unused helper method from last commit --- primed/miscellaneous_workspaces/tests/test_views.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index b8ac42ed..a71458a8 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -70,16 +70,6 @@ def get_url(self, *args): """Get the url for the view being tested.""" return reverse("anvil_consortium_manager:workspaces:new", args=args) - def get_api_url(self, billing_project_name, workspace_name): - """Return the Terra API url for a given billing project and workspace.""" - return ( - self.api_client.rawls_entry_point - + "/api/workspaces/" - + billing_project_name - + "/" - + workspace_name - ) - def test_creates_workspace(self): """Posting valid data to the form creates a workspace data object when using a custom adapter.""" billing_project = BillingProjectFactory.create(name="test-billing-project") From 21d0195d7e125e796bc58b0f3c036aeeb7b70137 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 10:52:03 -0800 Subject: [PATCH 24/27] Remove unrelated, unused comment from other test method Not really thematically part of this branch, but cleans up test coverage. --- primed/miscellaneous_workspaces/tests/test_forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/primed/miscellaneous_workspaces/tests/test_forms.py b/primed/miscellaneous_workspaces/tests/test_forms.py index e2ff1f05..033443e8 100644 --- a/primed/miscellaneous_workspaces/tests/test_forms.py +++ b/primed/miscellaneous_workspaces/tests/test_forms.py @@ -366,7 +366,6 @@ def test_valid_data_url(self): def test_invalid_data_url_is_not_url(self): """Form is invalid if data_url is not a valid url.""" - """Form is invalid when missing data_source.""" form_data = { "workspace": self.workspace, "requested_by": self.requester, From 5fd7a5b0de6c7bf55d097bb66024ceff33e1ed71 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 14:06:40 -0800 Subject: [PATCH 25/27] Fix table displaying target workspace name --- primed/miscellaneous_workspaces/tables.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/primed/miscellaneous_workspaces/tables.py b/primed/miscellaneous_workspaces/tables.py index 7f4feb52..824c871a 100644 --- a/primed/miscellaneous_workspaces/tables.py +++ b/primed/miscellaneous_workspaces/tables.py @@ -46,12 +46,15 @@ class DataPrepWorkspaceTable(tables.Table): """Class to render a table of Workspace objects with DataPrepWorkspace workspace data.""" name = tables.columns.Column(linkify=True) - target_workspace = tables.columns.Column(linkify=True) + # TODO: Figure out why this is not showing up + dataprepworkspace__target_workspace__name = tables.columns.Column( + linkify=True, verbose_name="Target workspace" + ) class Meta: model = Workspace fields = ( "name", - "target_workspace", + "dataprepworkspace__target_workspace__name", ) order_by = ("name",) From 64c83317989842d29cc84f7af0575d8eaca9207c Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 15:45:35 -0800 Subject: [PATCH 26/27] Add is_active field to DataPrepWorkspace model Update the migration. Show the is_active field in the table. Show the is_active status on the detail page. Add the field to the workspace data form. --- primed/miscellaneous_workspaces/forms.py | 1 + ...aprepworkspace_historicaldataprepworkspace.py | 6 ++++-- primed/miscellaneous_workspaces/models.py | 4 ++++ primed/miscellaneous_workspaces/tables.py | 9 ++++++++- .../miscellaneous_workspaces/tests/test_views.py | 16 ++++++++++++++++ .../dataprepworkspace_detail.html | 7 +++++++ 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/primed/miscellaneous_workspaces/forms.py b/primed/miscellaneous_workspaces/forms.py index 58946b5b..42856cfc 100644 --- a/primed/miscellaneous_workspaces/forms.py +++ b/primed/miscellaneous_workspaces/forms.py @@ -117,6 +117,7 @@ class Meta: "workspace", "target_workspace", "requested_by", + "is_active", ) # widgets = { # "studies": autocomplete.ModelSelect2Multiple( diff --git a/primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py b/primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py index 642c6548..eace0215 100644 --- a/primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py +++ b/primed/miscellaneous_workspaces/migrations/0008_dataprepworkspace_historicaldataprepworkspace.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.19 on 2023-11-07 18:52 +# Generated by Django 3.2.19 on 2023-11-09 22:11 from django.conf import settings from django.db import migrations, models @@ -10,7 +10,7 @@ class Migration(migrations.Migration): dependencies = [ - ('anvil_consortium_manager', '0013_alter_anvilprojectmanageraccess_options'), + ('anvil_consortium_manager', '0015_add_new_permissions'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('miscellaneous_workspaces', '0007_openaccessworkspace_data_url'), ] @@ -22,6 +22,7 @@ class Migration(migrations.Migration): ('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('is_active', models.BooleanField(default=True, help_text='Indicator of whether this workspace is currently being used for data preparation.')), ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField(db_index=True)), ('history_change_reason', models.CharField(max_length=100, null=True)), @@ -45,6 +46,7 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('is_active', models.BooleanField(default=True, help_text='Indicator of whether this workspace is currently being used for data preparation.')), ('requested_by', models.ForeignKey(help_text='The user who requested creation.', on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), ('target_workspace', models.ForeignKey(help_text='The workspace for which data is being prepared or updated.', on_delete=django.db.models.deletion.PROTECT, related_name='data_prep_workspaces', to='anvil_consortium_manager.workspace')), ('workspace', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='anvil_consortium_manager.workspace')), diff --git a/primed/miscellaneous_workspaces/models.py b/primed/miscellaneous_workspaces/models.py index 40c8103c..7329551f 100644 --- a/primed/miscellaneous_workspaces/models.py +++ b/primed/miscellaneous_workspaces/models.py @@ -73,6 +73,10 @@ class DataPrepWorkspace(RequesterModel, TimeStampedModel, BaseWorkspaceData): related_name="data_prep_workspaces", help_text="The workspace for which data is being prepared or updated.", ) + is_active = models.BooleanField( + default=True, + help_text="Indicator of whether this workspace is currently being used to prepare data.", + ) def clean(self): if hasattr(self, "target_workspace"): diff --git a/primed/miscellaneous_workspaces/tables.py b/primed/miscellaneous_workspaces/tables.py index 824c871a..f9763621 100644 --- a/primed/miscellaneous_workspaces/tables.py +++ b/primed/miscellaneous_workspaces/tables.py @@ -3,7 +3,10 @@ import django_tables2 as tables from anvil_consortium_manager.models import Workspace -from primed.primed_anvil.tables import WorkspaceSharedWithConsortiumColumn +from primed.primed_anvil.tables import ( + BooleanIconColumn, + WorkspaceSharedWithConsortiumColumn, +) class OpenAccessWorkspaceTable(tables.Table): @@ -50,11 +53,15 @@ class DataPrepWorkspaceTable(tables.Table): dataprepworkspace__target_workspace__name = tables.columns.Column( linkify=True, verbose_name="Target workspace" ) + dataprepworkspace__is_active = BooleanIconColumn( + verbose_name="Active?", show_false_icon=True + ) class Meta: model = Workspace fields = ( "name", "dataprepworkspace__target_workspace__name", + "dataprepworkspace__is_active", ) order_by = ("name",) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index a71458a8..5f623bb4 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -1126,6 +1126,22 @@ def test_status_code_with_user_permission(self): # Includes link to target workspace. self.assertContains(response, obj.target_workspace.get_absolute_url()) + def test_template_active(self): + """Returns successful response code.""" + obj = factories.DataPrepWorkspaceFactory.create() + self.client.force_login(self.user) + response = self.client.get(obj.workspace.get_absolute_url()) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Active") + + def test_template_inactive(self): + """Returns successful response code.""" + obj = factories.DataPrepWorkspaceFactory.create(is_active=False) + self.client.force_login(self.user) + response = self.client.get(obj.workspace.get_absolute_url()) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Inactive") + class DataPrepWorkspaceCreateTest(AnVILAPIMockTestMixin, TestCase): """Tests of the WorkspaceCreate view from ACM with this app's DataPrepWorkspace model.""" diff --git a/primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html b/primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html index f824351c..7ff1c45a 100644 --- a/primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html +++ b/primed/templates/miscellaneous_workspaces/dataprepworkspace_detail.html @@ -9,5 +9,12 @@
    Target type
    {{ object.dataprepworkspace.target_workspace.workspace_type }}
    +
    Status
    + {% if object.dataprepworkspace.is_active %} + Active + {% else %} + Inactive + {% endif %} +

    {% endblock workspace_data %} From fceeb388dbd8b63d9b9e07dd54e6971dfbf037cc Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Thu, 9 Nov 2023 15:46:49 -0800 Subject: [PATCH 27/27] Update ACM version to new patch version in requirements file The patch version also has a bugfix for the WorkspaceUpdate and WorkspaceClone views for workspace data objects that have a second foreign key to Workspace. --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 70dafea2..2c3def81 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -22,7 +22,7 @@ django-dbbackup==4.0.1 # https://github.com/jazzband/django-dbbackup django-extensions==3.2.1 # https://github.com/django-extensions/django-extensions # anvil_consortium_manager -git+https://github.com/UW-GAC/django-anvil-consortium-manager.git@v0.20 +git+https://github.com/UW-GAC/django-anvil-consortium-manager.git@v0.20.1 # Simple history - model history tracking django-simple-history==3.1.1