diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 842469d1..3b9bd274 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 3.9.2 + rev: 7.0.0 hooks: - id: flake8 args: ['--config=setup.cfg'] diff --git a/add_phenotype_inventory_input_example_data.py b/add_phenotype_inventory_input_example_data.py new file mode 100644 index 00000000..427c6477 --- /dev/null +++ b/add_phenotype_inventory_input_example_data.py @@ -0,0 +1,50 @@ +# Temporary script to create some test data. +# Run with: python manage.py shell < add_phenotype_inventory_input_example_data.py + +from anvil_consortium_manager.tests.factories import ( + ManagedGroupFactory, + WorkspaceGroupSharingFactory, +) + +from primed.cdsa.tests.factories import CDSAWorkspaceFactory +from primed.dbgap.tests.factories import dbGaPWorkspaceFactory +from primed.miscellaneous_workspaces.tests.factories import OpenAccessWorkspaceFactory +from primed.primed_anvil.tests.factories import StudyFactory + +# Create a dbGaP workspace. +fhs = StudyFactory.create(short_name="FHS", full_name="Framingham Heart Study") +workspace_dbgap = dbGaPWorkspaceFactory.create( + dbgap_study_accession__dbgap_phs=7, + dbgap_study_accession__studies=[fhs], + dbgap_version=33, + dbgap_participant_set=12, + dbgap_consent_code=1, + dbgap_consent_abbreviation="HMB", + workspace__name="DBGAP_FHS_v33_p12_HMB", +) + + +# Create a CDSA workspace. +workspace_cdsa = CDSAWorkspaceFactory.create( + study__short_name="MESA", + workspace__name="CDSA_MESA_HMB", +) + +# Create an open access workspace +workspace_open_access = OpenAccessWorkspaceFactory.create( + workspace__name="OPEN_ACCESS_FHS", +) +workspace_open_access.studies.add(fhs) + + +# Share workspaces with PRIMED_ALL +primed_all = ManagedGroupFactory.create(name="PRIMED_ALL") +WorkspaceGroupSharingFactory.create( + workspace=workspace_dbgap.workspace, group=primed_all +) +WorkspaceGroupSharingFactory.create( + workspace=workspace_cdsa.workspace, group=primed_all +) +WorkspaceGroupSharingFactory.create( + workspace=workspace_open_access.workspace, group=primed_all +) diff --git a/primed/primed_anvil/helpers.py b/primed/primed_anvil/helpers.py index 39193ade..e0f6cacf 100644 --- a/primed/primed_anvil/helpers.py +++ b/primed/primed_anvil/helpers.py @@ -1,6 +1,9 @@ +from itertools import groupby + import pandas as pd -from anvil_consortium_manager.models import WorkspaceGroupSharing -from django.db.models import Exists, F, OuterRef, Value +from anvil_consortium_manager.models import ManagedGroup, WorkspaceGroupSharing +from django.db.models import CharField, Exists, F, OuterRef, Value +from django.db.models.functions import Concat from primed.cdsa.models import CDSAWorkspace from primed.dbgap.models import dbGaPWorkspace @@ -127,3 +130,96 @@ def get_summary_table_data(): # Convert to a list of dictionaries for passing to the django-tables2 table. data = data.to_dict(orient="records") return data + + +def get_workspaces_for_phenotype_inventory(): + """Get input to the primed-phenotype-inventory workflow. + + This function generates the input for the "workspaces" field of the primed-phenotype-inventory workflow. Only + workspaces that have been shared with the consortium are included. + See dockstore link: https://dockstore.org/workflows/github.com/UW-GAC/primed-inventory-workflows/primed_phenotype_inventory:main?tab=info + + The "workspaces" field has the format: + { + "billing-project-1/workspace-1": "study1, study2", + "billing-project-2/workspace-2": "study3", + ... + } + """ # noqa: E501 + + # primed-all group. We will need this to determine if the workspace is shared with PRIMED_ALL. + primed_all = ManagedGroup.objects.get(name="PRIMED_ALL") + + dbgap_workspaces = ( + dbGaPWorkspace.objects.filter( + # Just those that are shared with PRIMED_ALL. + workspace__workspacegroupsharing__group=primed_all, + ) + .annotate( + workspace_name=Concat( + F("workspace__billing_project__name"), + Value("/"), + F("workspace__name"), + output_field=CharField(), + ), + study_names=F("dbgap_study_accession__studies__short_name"), + ) + .values( + # "workspace", + # "workspace_billing_project", + "workspace_name", + "study_names", + ) + ) + + cdsa_workspaces = ( + CDSAWorkspace.objects.filter( + # Just those that are shared with PRIMED_ALL. + workspace__workspacegroupsharing__group=primed_all, + ) + .annotate( + workspace_name=Concat( + F("workspace__billing_project__name"), + Value("/"), + F("workspace__name"), + output_field=CharField(), + ), + study_names=F("study__short_name"), + ) + .values( + "workspace_name", + "study_names", + ) + ) + + open_access_workspaces = ( + OpenAccessWorkspace.objects.filter( + # Just those that are shared with PRIMED_ALL. + workspace__workspacegroupsharing__group=primed_all, + ) + .annotate( + workspace_name=Concat( + F("workspace__billing_project__name"), + Value("/"), + F("workspace__name"), + output_field=CharField(), + ), + study_names=F("studies__short_name"), + ) + .values( + "workspace_name", + "study_names", + ) + ) + + # Combine all querysets and process into the expected output for the AnVIL workflow. + workspaces = dbgap_workspaces.union(cdsa_workspaces).union(open_access_workspaces) + + json = {} + for key, group in groupby(workspaces, lambda x: x["workspace_name"]): + study_names = [x["study_names"] if x["study_names"] else "" for x in group] + if not study_names: + study_names = "" + json[key] = ", ".join(sorted(study_names)) + + return json diff --git a/primed/primed_anvil/tests/test_helpers.py b/primed/primed_anvil/tests/test_helpers.py index 78a20b57..a5bf54f1 100644 --- a/primed/primed_anvil/tests/test_helpers.py +++ b/primed/primed_anvil/tests/test_helpers.py @@ -1,10 +1,16 @@ """Tests of helper functions in the `primed_anvil` app.""" -from anvil_consortium_manager.tests.factories import WorkspaceGroupSharingFactory +from anvil_consortium_manager.tests.factories import ( + ManagedGroupFactory, + WorkspaceGroupSharingFactory, +) from django.test import TestCase from primed.cdsa.tests.factories import CDSAWorkspaceFactory -from primed.dbgap.tests.factories import dbGaPWorkspaceFactory +from primed.dbgap.tests.factories import ( + dbGaPStudyAccessionFactory, + dbGaPWorkspaceFactory, +) from primed.miscellaneous_workspaces.tests.factories import OpenAccessWorkspaceFactory from primed.primed_anvil.tests.factories import AvailableDataFactory, StudyFactory @@ -544,3 +550,290 @@ def test_one_cdsa_workspace_one_open_access_workspace_different_available_data( }, res, ) + + +class GetWorkspacesForPhenotypeInventoryTest(TestCase): + """Tests for the helpers.get_workspaces_for_phenotype_inventory method.""" + + def setUp(self): + """Set up the test case.""" + super().setUp() + # Create the PRIMED_ALL group. + self.primed_all_group = ManagedGroupFactory.create(name="PRIMED_ALL") + + def test_no_workspaces(self): + """get_workspaces_for_phenotype_inventory with no workspaces.""" + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(res, {}) + + def test_one_dbgap_workspace_not_shared(self): + """get_workspaces_for_phenotype_inventory with one dbGaP workspace.""" + dbGaPWorkspaceFactory.create() + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(res, {}) + + def test_one_dbgap_workspace_shared_one_study(self): + """get_workspaces_for_phenotype_inventory with one dbGaP workspace.""" + study = StudyFactory.create(short_name="TEST") + workspace = dbGaPWorkspaceFactory.create( + workspace__billing_project__name="test-bp", + workspace__name="test-ws", + dbgap_study_accession__studies=[study], + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 1) + self.assertIn("test-bp/test-ws", res) + self.assertEqual(res["test-bp/test-ws"], "TEST") + + def test_one_dbgap_workspace_shared_two_studies(self): + """get_workspaces_for_phenotype_inventory with one dbGaP workspace.""" + study_1 = StudyFactory.create(short_name="TEST_2") + study_2 = StudyFactory.create(short_name="TEST_1") + study_accession = dbGaPStudyAccessionFactory.create(studies=[study_1, study_2]) + workspace = dbGaPWorkspaceFactory.create( + workspace__billing_project__name="test-bp", + workspace__name="test-ws", + dbgap_study_accession=study_accession, + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 1) + self.assertIn("test-bp/test-ws", res) + self.assertEqual(res["test-bp/test-ws"], "TEST_1, TEST_2") + + def test_two_dbgap_workspaces(self): + """get_workspaces_for_phenotype_inventory with two dbGaP workspaces.""" + study_1 = StudyFactory.create(short_name="TEST 1") + workspace_1 = dbGaPWorkspaceFactory.create( + workspace__billing_project__name="test-bp-1", + workspace__name="test-ws-1", + dbgap_study_accession__studies=[study_1], + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace_1.workspace, group=self.primed_all_group + ) + study_2 = StudyFactory.create(short_name="TEST 2") + workspace_2 = dbGaPWorkspaceFactory.create( + workspace__billing_project__name="test-bp-2", + workspace__name="test-ws-2", + dbgap_study_accession__studies=[study_2], + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace_2.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 2) + self.assertIn("test-bp-1/test-ws-1", res) + self.assertEqual(res["test-bp-1/test-ws-1"], "TEST 1") + self.assertIn("test-bp-2/test-ws-2", res) + self.assertEqual(res["test-bp-2/test-ws-2"], "TEST 2") + + def test_one_cdsa_workspace_not_shared(self): + """get_workspaces_for_phenotype_inventory with one CDSA workspace.""" + CDSAWorkspaceFactory.create() + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(res, {}) + + def test_one_cdsa_workspace_shared_one_study(self): + """get_workspaces_for_phenotype_inventory with one CDSA workspace.""" + study = StudyFactory.create(short_name="TEST") + workspace = CDSAWorkspaceFactory.create( + workspace__billing_project__name="test-bp", + workspace__name="test-ws", + study=study, + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 1) + self.assertIn("test-bp/test-ws", res) + self.assertEqual(res["test-bp/test-ws"], "TEST") + + def test_two_cdsa_workspaces(self): + """get_workspaces_for_phenotype_inventory with two CDSA workspaces.""" + study_1 = StudyFactory.create(short_name="TEST 1") + workspace_1 = CDSAWorkspaceFactory.create( + workspace__billing_project__name="test-bp-1", + workspace__name="test-ws-1", + study=study_1, + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace_1.workspace, group=self.primed_all_group + ) + study_2 = StudyFactory.create(short_name="TEST 2") + workspace_2 = CDSAWorkspaceFactory.create( + workspace__billing_project__name="test-bp-2", + workspace__name="test-ws-2", + study=study_2, + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace_2.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 2) + self.assertIn("test-bp-1/test-ws-1", res) + self.assertEqual(res["test-bp-1/test-ws-1"], "TEST 1") + self.assertIn("test-bp-2/test-ws-2", res) + self.assertEqual(res["test-bp-2/test-ws-2"], "TEST 2") + + def test_one_open_access_workspace_not_shared(self): + """get_workspaces_for_phenotype_inventory with one dbGaP workspace.""" + OpenAccessWorkspaceFactory.create() + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(res, {}) + + def test_one_open_access_workspace_shared_no_study(self): + """get_workspaces_for_phenotype_inventory with one Open access workspace.""" + workspace = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp", + workspace__name="test-ws", + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 1) + self.assertIn("test-bp/test-ws", res) + self.assertEqual(res["test-bp/test-ws"], "") + + def test_one_open_access_workspace_shared_one_study(self): + """get_workspaces_for_phenotype_inventory with one Open access workspace.""" + study = StudyFactory.create(short_name="TEST") + workspace = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp", + workspace__name="test-ws", + ) + workspace.studies.add(study) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 1) + self.assertIn("test-bp/test-ws", res) + self.assertEqual(res["test-bp/test-ws"], "TEST") + + def test_one_open_access_workspace_shared_two_studies(self): + """get_workspaces_for_phenotype_inventory with one Open access workspace.""" + study_1 = StudyFactory.create(short_name="TEST_2") + study_2 = StudyFactory.create(short_name="TEST_1") + workspace = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp", + workspace__name="test-ws", + ) + workspace.studies.add(study_1, study_2) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 1) + self.assertIn("test-bp/test-ws", res) + self.assertEqual(res["test-bp/test-ws"], "TEST_1, TEST_2") + + def test_two_open_access_workspaces(self): + """get_workspaces_for_phenotype_inventory with two Open access workspace.""" + workspace_1 = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp-1", + workspace__name="test-ws-1", + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace_1.workspace, group=self.primed_all_group + ) + study_2 = StudyFactory.create(short_name="TEST 2") + workspace_2 = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp-2", + workspace__name="test-ws-2", + ) + workspace_2.studies.add(study_2) + WorkspaceGroupSharingFactory.create( + workspace=workspace_2.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 2) + self.assertIn("test-bp-1/test-ws-1", res) + self.assertEqual(res["test-bp-1/test-ws-1"], "") + self.assertIn("test-bp-2/test-ws-2", res) + self.assertEqual(res["test-bp-2/test-ws-2"], "TEST 2") + + def test_multiple_workspace_types_same_study(self): + study = StudyFactory.create(short_name="TEST") + # dbgap + workspace = dbGaPWorkspaceFactory.create( + workspace__billing_project__name="test-bp-dbgap", + workspace__name="test-ws-dbgap", + dbgap_study_accession__studies=[study], + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + # CDSA + workspace = CDSAWorkspaceFactory.create( + workspace__billing_project__name="test-bp-cdsa", + workspace__name="test-ws-cdsa", + study=study, + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + # Open access + workspace = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp-open", + workspace__name="test-ws-open", + ) + workspace.studies.add(study) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 3) + self.assertIn("test-bp-dbgap/test-ws-dbgap", res) + self.assertEqual(res["test-bp-dbgap/test-ws-dbgap"], "TEST") + self.assertIn("test-bp-cdsa/test-ws-cdsa", res) + self.assertEqual(res["test-bp-cdsa/test-ws-cdsa"], "TEST") + self.assertIn("test-bp-open/test-ws-open", res) + self.assertEqual(res["test-bp-open/test-ws-open"], "TEST") + + def test_multiple_workspace_types_separate_studies(self): + study_1 = StudyFactory.create(short_name="TEST 1") + # dbgap + workspace = dbGaPWorkspaceFactory.create( + workspace__billing_project__name="test-bp-dbgap", + workspace__name="test-ws-dbgap", + dbgap_study_accession__studies=[study_1], + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + # CDSA + study_2 = StudyFactory.create(short_name="TEST 2") + workspace = CDSAWorkspaceFactory.create( + workspace__billing_project__name="test-bp-cdsa", + workspace__name="test-ws-cdsa", + study=study_2, + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + # Open access + study_3 = StudyFactory.create(short_name="TEST 3") + workspace = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp-open", + workspace__name="test-ws-open", + ) + workspace.studies.add(study_3) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all_group + ) + res = helpers.get_workspaces_for_phenotype_inventory() + self.assertEqual(len(res), 3) + self.assertIn("test-bp-dbgap/test-ws-dbgap", res) + self.assertEqual(res["test-bp-dbgap/test-ws-dbgap"], "TEST 1") + self.assertIn("test-bp-cdsa/test-ws-cdsa", res) + self.assertEqual(res["test-bp-cdsa/test-ws-cdsa"], "TEST 2") + self.assertIn("test-bp-open/test-ws-open", res) + self.assertEqual(res["test-bp-open/test-ws-open"], "TEST 3") diff --git a/primed/primed_anvil/tests/test_views.py b/primed/primed_anvil/tests/test_views.py index 9a95ea11..b800b6df 100644 --- a/primed/primed_anvil/tests/test_views.py +++ b/primed/primed_anvil/tests/test_views.py @@ -1,7 +1,11 @@ import json from anvil_consortium_manager import models as acm_models -from anvil_consortium_manager.tests.factories import AccountFactory +from anvil_consortium_manager.tests.factories import ( + AccountFactory, + ManagedGroupFactory, + WorkspaceGroupSharingFactory, +) from anvil_consortium_manager.views import AccountList from constance.test import override_config from django.conf import settings @@ -1267,3 +1271,81 @@ def test_includes_cdsa_workspaces(self): response = self.client.get(self.get_url()) self.assertIn("summary_table", response.context_data) self.assertEqual(len(response.context_data["summary_table"].rows), 1) + + +class PhenotypeInventoryInputsViewTest(TestCase): + """Tests for the PhenotypeInventoryInputsView 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") + self.user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME + ) + ) + self.primed_all = ManagedGroupFactory.create(name="PRIMED_ALL") + + def get_url(self, *args): + """Get the url for the view being tested.""" + return reverse("primed_anvil:utilities:phenotype_inventory_inputs", args=args) + + def get_view(self): + """Return the view being tested.""" + return views.PhenotypeInventoryInputsView.as_view() + + def test_view_redirect_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.assertRedirects( + response, resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url() + ) + + def test_status_code_with_authenticated_user(self): + """Redirects to login view when user has no perms.""" + user_no_perms = User.objects.create_user( + username="test-none", password="test-none" + ) + request = self.factory.get(self.get_url()) + request.user = user_no_perms + with self.assertRaises(PermissionDenied): + self.get_view()(request) + + def test_status_code_with_view_user(self): + """Redirects to login view when user has view perm.""" + user = User.objects.create_user(username="test-none", password="test-none") + user.user_permissions.add( + Permission.objects.get( + codename=acm_models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME + ) + ) + request = self.factory.get(self.get_url()) + request.user = user + with self.assertRaises(PermissionDenied): + self.get_view()(request) + + def test_context_workspaces_input_no_workspaces(self): + """workspaces_input exists in the context and is correct with no workspaces.""" + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertIn("workspaces_input", response.context_data) + self.assertEqual(response.context_data["workspaces_input"], "{}") + + def test_context_workspaces_input_one_workspace(self): + """workspaces_input exists in the context and is correct with one shared workspace.""" + workspace = OpenAccessWorkspaceFactory.create( + workspace__billing_project__name="test-bp", workspace__name="test-ws" + ) + WorkspaceGroupSharingFactory.create( + workspace=workspace.workspace, group=self.primed_all + ) + self.client.force_login(self.user) + response = self.client.get(self.get_url()) + self.assertIn("workspaces_input", response.context_data) + self.assertEqual( + response.context_data["workspaces_input"], + """{\n "test-bp/test-ws": ""\n}""", + ) diff --git a/primed/primed_anvil/urls.py b/primed/primed_anvil/urls.py index 909ad7fb..81557dcf 100644 --- a/primed/primed_anvil/urls.py +++ b/primed/primed_anvil/urls.py @@ -37,9 +37,21 @@ "summaries", ) +utilities_patterns = ( + [ + path( + "phenotype_inventory_inputs/", + views.PhenotypeInventoryInputsView.as_view(), + name="phenotype_inventory_inputs", + ), + ], + "utilities", +) + urlpatterns = [ path("studies/", include(study_patterns)), path("study_sites/", include(study_site_patterns)), path("available_data/", include(available_data_patterns)), path("summaries/", include(summary_patterns)), + path("utilities/", include(utilities_patterns)), ] diff --git a/primed/primed_anvil/views.py b/primed/primed_anvil/views.py index c89ab3cb..f85e787b 100644 --- a/primed/primed_anvil/views.py +++ b/primed/primed_anvil/views.py @@ -1,3 +1,5 @@ +import json + from anvil_consortium_manager.auth import ( AnVILConsortiumManagerStaffEditRequired, AnVILConsortiumManagerStaffViewRequired, @@ -183,3 +185,17 @@ def get_context_data(self, **kwargs): table_data = helpers.get_summary_table_data() context["summary_table"] = tables.DataSummaryTable(table_data) return context + + +class PhenotypeInventoryInputsView( + AnVILConsortiumManagerStaffViewRequired, TemplateView +): + + template_name = "primed_anvil/phenotype_inventory_inputs.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["workspaces_input"] = json.dumps( + helpers.get_workspaces_for_phenotype_inventory(), indent=2 + ) + return context diff --git a/primed/templates/primed_anvil/phenotype_inventory_inputs.html b/primed/templates/primed_anvil/phenotype_inventory_inputs.html new file mode 100644 index 00000000..7dc372ce --- /dev/null +++ b/primed/templates/primed_anvil/phenotype_inventory_inputs.html @@ -0,0 +1,57 @@ +{% extends 'base.html' %} +{% load render_table from django_tables2 %} + +{% block extra_navbar %} + {% include 'anvil_consortium_manager/navbar.html' %} +{% endblock %} + +{% block content %} + +
+ This page creates the input for the "workspaces" field in the + PRIMED phenotype inventory workflow. + Copy the text in the box below and paste it into the "workspaces" field when running the workflow on AnVIL. +
+ + ++
{{ workspaces_input }}
+
+