From 514425cb6af1683ba08fff5e93095a0162bc74bf Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Fri, 22 Mar 2024 16:14:41 -0700 Subject: [PATCH 1/3] Change intended_workspace_type to intended_usage For TemplateWorkspaces, remove the intended_workspace_type field and add an intended_usage field instead. This is helpful because a template workspace can be cloned to create one of a couple workspace types (eg, the genotype template is used for both CDSA and dbGaP). --- ...ended_workspace_type_add_intended_usage.py | 33 ++++++++ primed/miscellaneous_workspaces/models.py | 24 +----- .../tests/factories.py | 15 +--- .../tests/test_models.py | 79 +------------------ 4 files changed, 38 insertions(+), 113 deletions(-) create mode 100644 primed/miscellaneous_workspaces/migrations/0011_alter_templateworkspace_remove_intended_workspace_type_add_intended_usage.py diff --git a/primed/miscellaneous_workspaces/migrations/0011_alter_templateworkspace_remove_intended_workspace_type_add_intended_usage.py b/primed/miscellaneous_workspaces/migrations/0011_alter_templateworkspace_remove_intended_workspace_type_add_intended_usage.py new file mode 100644 index 00000000..f632dc22 --- /dev/null +++ b/primed/miscellaneous_workspaces/migrations/0011_alter_templateworkspace_remove_intended_workspace_type_add_intended_usage.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.11 on 2024-03-22 22:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("miscellaneous_workspaces", "0010_update_workspace_type_field"), + ] + + operations = [ + migrations.RemoveField( + model_name="historicaltemplateworkspace", + name="intended_workspace_type", + ), + migrations.RemoveField( + model_name="templateworkspace", + name="intended_workspace_type", + ), + migrations.AddField( + model_name="historicaltemplateworkspace", + name="intended_usage", + field=models.TextField(default=""), + preserve_default=False, + ), + migrations.AddField( + model_name="templateworkspace", + name="intended_usage", + field=models.TextField(default=""), + preserve_default=False, + ), + ] diff --git a/primed/miscellaneous_workspaces/models.py b/primed/miscellaneous_workspaces/models.py index 210bae3b..d702122d 100644 --- a/primed/miscellaneous_workspaces/models.py +++ b/primed/miscellaneous_workspaces/models.py @@ -1,6 +1,5 @@ """Model definitions for the `miscellaneous_workspaces` app.""" -from anvil_consortium_manager.adapters.workspace import workspace_adapter_registry from anvil_consortium_manager.models import BaseWorkspaceData, Workspace from django.core.exceptions import ValidationError from django.db import models @@ -24,28 +23,7 @@ class ResourceWorkspace(RequesterModel, TimeStampedModel, BaseWorkspaceData): class TemplateWorkspace(TimeStampedModel, BaseWorkspaceData): """A model to track template workspaces.""" - intended_workspace_type = models.CharField(max_length=63) - - def clean(self): - """Custom cleaning checks. - - - Verify that intended_workspace_type is one of the registered types, excluding this type.""" - registered_workspace_types = workspace_adapter_registry.get_registered_names() - if self.intended_workspace_type: - if self.intended_workspace_type not in registered_workspace_types: - raise ValidationError( - { - "intended_workspace_type": "intended_workspace_type must be one of the registered types." - } - ) - # We cannot import the adapter here because it would lead to a circular import, but we don't want - # to create a template workspace for TemplateWorkspaces. So check if the type is not "template". - elif self.intended_workspace_type == "template": - raise ValidationError( - { - "intended_workspace_type": "intended_workspace_type may not be 'template'." - } - ) + intended_usage = models.TextField() class OpenAccessWorkspace(RequesterModel, TimeStampedModel, BaseWorkspaceData): diff --git a/primed/miscellaneous_workspaces/tests/factories.py b/primed/miscellaneous_workspaces/tests/factories.py index c562c36a..a68d9e54 100644 --- a/primed/miscellaneous_workspaces/tests/factories.py +++ b/primed/miscellaneous_workspaces/tests/factories.py @@ -1,8 +1,5 @@ -import random - -from anvil_consortium_manager.adapters.workspace import workspace_adapter_registry from anvil_consortium_manager.tests.factories import WorkspaceFactory -from factory import Faker, SubFactory, lazy_attribute +from factory import Faker, SubFactory from factory.django import DjangoModelFactory from primed.users.tests.factories import UserFactory @@ -53,19 +50,11 @@ class TemplateWorkspaceFactory(DjangoModelFactory): WorkspaceFactory, workspace_type=adapters.TemplateWorkspaceAdapter().get_type(), ) + intended_usage = Faker("sentence") class Meta: model = models.TemplateWorkspace - @lazy_attribute - def intended_workspace_type(self): - """Select a random registered workspace_type other than template.""" - registered_types = list( - workspace_adapter_registry.get_registered_adapters().keys() - ) - registered_types.remove(adapters.TemplateWorkspaceAdapter().get_type()) - return random.choice(registered_types) - class OpenAccessWorkspaceFactory(DjangoModelFactory): """A factory for the OpenAccessWorkspace model.""" diff --git a/primed/miscellaneous_workspaces/tests/test_models.py b/primed/miscellaneous_workspaces/tests/test_models.py index 7b38836c..c8222656 100644 --- a/primed/miscellaneous_workspaces/tests/test_models.py +++ b/primed/miscellaneous_workspaces/tests/test_models.py @@ -7,7 +7,7 @@ from primed.primed_anvil.tests.factories import AvailableDataFactory, StudyFactory from primed.users.tests.factories import UserFactory -from .. import adapters, models +from .. import models from . import factories @@ -79,7 +79,7 @@ class TemplateWorkspaceTest(TestCase): def test_model_saving(self): """Creation using the model constructor and .save() works.""" workspace = WorkspaceFactory.create() - instance = models.TemplateWorkspace(workspace=workspace) + instance = models.TemplateWorkspace(workspace=workspace, intended_usage="Test") instance.save() self.assertIsInstance(instance, models.TemplateWorkspace) @@ -91,81 +91,6 @@ def test_str_method(self): self.assertIsInstance(str(instance), str) self.assertEqual(str(instance), "test-bp/test-ws") - def test_clean_missing_intended_workspace_type_missing(self): - workspace = WorkspaceFactory.create( - billing_project__name="test-bp", name="test-ws" - ) - instance = models.TemplateWorkspace(workspace=workspace) - with self.assertRaises(ValidationError) as e: - instance.full_clean() - self.assertEqual(len(e.exception.message_dict), 1) - self.assertIn("intended_workspace_type", e.exception.message_dict) - self.assertEqual(len(e.exception.message_dict["intended_workspace_type"]), 1) - self.assertIn( - "cannot be blank", e.exception.message_dict["intended_workspace_type"][0] - ) - - def test_clean_intended_workspace_type_blank(self): - workspace = WorkspaceFactory.create( - billing_project__name="test-bp", name="test-ws" - ) - instance = factories.TemplateWorkspaceFactory.build( - workspace=workspace, - intended_workspace_type="", - ) - with self.assertRaises(ValidationError) as e: - instance.full_clean() - self.assertEqual(len(e.exception.message_dict), 1) - self.assertIn("intended_workspace_type", e.exception.message_dict) - self.assertEqual(len(e.exception.message_dict["intended_workspace_type"]), 1) - self.assertIn( - "cannot be blank", e.exception.message_dict["intended_workspace_type"][0] - ) - - def test_clean_intended_workspace_type_with_registered_adapter(self): - """No ValidationError is raised if intended_workspace_type is a registered type.""" - workspace = WorkspaceFactory.create( - billing_project__name="test-bp", name="test-ws" - ) - instance = factories.TemplateWorkspaceFactory.build(workspace=workspace) - instance.full_clean() - - def test_clean_intended_workspace_type_with_unregistered_adapter(self): - """ValidationError is raised if intended_workspace_type is not a registered type.""" - workspace = WorkspaceFactory.create( - billing_project__name="test-bp", name="test-ws" - ) - instance = factories.TemplateWorkspaceFactory.build( - workspace=workspace, intended_workspace_type="foo" - ) - with self.assertRaises(ValidationError) as e: - instance.full_clean() - self.assertEqual(len(e.exception.message_dict), 1) - self.assertIn("intended_workspace_type", e.exception.message_dict) - self.assertEqual(len(e.exception.message_dict["intended_workspace_type"]), 1) - self.assertIn( - "registered types", e.exception.message_dict["intended_workspace_type"][0] - ) - - def test_clean_intended_workspace_type_template(self): - """ValidationError is raised if intended_workspace_type is set to "template".""" - workspace = WorkspaceFactory.create( - billing_project__name="test-bp", name="test-ws" - ) - template_workspace_type = adapters.TemplateWorkspaceAdapter().get_type() - instance = factories.TemplateWorkspaceFactory.build( - workspace=workspace, intended_workspace_type=template_workspace_type - ) - with self.assertRaises(ValidationError) as e: - instance.full_clean() - self.assertEqual(len(e.exception.message_dict), 1) - self.assertIn("intended_workspace_type", e.exception.message_dict) - self.assertEqual(len(e.exception.message_dict["intended_workspace_type"]), 1) - self.assertIn( - template_workspace_type, - e.exception.message_dict["intended_workspace_type"][0], - ) - class OpenAccessWorkspaceTest(TestCase): """Tests for the OpenAccessWorkspace model.""" From a6b6396516bc4f5525cbf3e5aa8797f92474f5ca Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Fri, 22 Mar 2024 16:21:26 -0700 Subject: [PATCH 2/3] Update forms for TemplateWorkspace intended_usage --- primed/miscellaneous_workspaces/forms.py | 15 +---- .../tests/test_forms.py | 62 ++++--------------- 2 files changed, 12 insertions(+), 65 deletions(-) diff --git a/primed/miscellaneous_workspaces/forms.py b/primed/miscellaneous_workspaces/forms.py index 6a0825ac..31eb2e46 100644 --- a/primed/miscellaneous_workspaces/forms.py +++ b/primed/miscellaneous_workspaces/forms.py @@ -1,6 +1,5 @@ """Forms for the `workspaces` app.""" -from anvil_consortium_manager.adapters.workspace import workspace_adapter_registry from anvil_consortium_manager.forms import Bootstrap5MediaFormMixin from dal import autocomplete from django import forms @@ -62,23 +61,11 @@ class Meta: class TemplateWorkspaceForm(forms.ModelForm): """Form for a TemplateWorkspace object.""" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # Set the intended_workspace_type options, excluding "template". - workspace_type_choices = [ - (key, value) - for key, value in workspace_adapter_registry.get_registered_names().items() - if key != "template" - ] - self.fields["intended_workspace_type"] = forms.ChoiceField( - choices=[("", "---------")] + workspace_type_choices - ) - class Meta: model = models.TemplateWorkspace fields = ( "workspace", - "intended_workspace_type", + "intended_usage", ) diff --git a/primed/miscellaneous_workspaces/tests/test_forms.py b/primed/miscellaneous_workspaces/tests/test_forms.py index 40b20431..b6dd125a 100644 --- a/primed/miscellaneous_workspaces/tests/test_forms.py +++ b/primed/miscellaneous_workspaces/tests/test_forms.py @@ -152,7 +152,7 @@ def test_valid(self): """Form is valid with necessary input.""" form_data = { "workspace": self.workspace, - "intended_workspace_type": "resource", + "intended_usage": "Test usage", } form = self.form_class(data=form_data) self.assertTrue(form.is_valid()) @@ -160,7 +160,7 @@ def test_valid(self): def test_invalid_missing_workspace(self): """Form is invalid when missing workspace.""" form_data = { - "intended_workspace_type": "resource", + "intended_usage": "Test usage", } form = self.form_class(data=form_data) self.assertFalse(form.is_valid()) @@ -169,7 +169,7 @@ def test_invalid_missing_workspace(self): self.assertEqual(len(form.errors["workspace"]), 1) self.assertIn("required", form.errors["workspace"][0]) - def test_invalid_missing_intended_workspace_type(self): + def test_invalid_missing_intended_usage(self): """Form is invalid if intended_workspace_type is missing.""" form_data = { "workspace": self.workspace, @@ -177,62 +177,22 @@ def test_invalid_missing_intended_workspace_type(self): form = self.form_class(data=form_data) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertIn("intended_workspace_type", form.errors) - self.assertEqual(len(form.errors["intended_workspace_type"]), 1) - self.assertIn("required", form.errors["intended_workspace_type"][0]) + self.assertIn("intended_usage", form.errors) + self.assertEqual(len(form.errors["intended_usage"]), 1) + self.assertIn("required", form.errors["intended_usage"][0]) - def test_invalid_blank_intended_workspace_type(self): + def test_invalid_blank_intended_usage(self): """Form is invalid if intended_workspace_type is missing.""" form_data = { "workspace": self.workspace, - "intended_workspace_type": "", + "intended_usage": "", } form = self.form_class(data=form_data) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertIn("intended_workspace_type", form.errors) - self.assertEqual(len(form.errors["intended_workspace_type"]), 1) - self.assertIn("required", form.errors["intended_workspace_type"][0]) - - def test_invalid_intended_workspace_type_template(self): - """Form is invalid if intended_workspace_type is "template".""" - form_data = { - "workspace": self.workspace, - "intended_workspace_type": "template", - } - form = self.form_class(data=form_data) - self.assertFalse(form.is_valid()) - self.assertEqual(len(form.errors), 1) - self.assertIn("intended_workspace_type", form.errors) - self.assertEqual(len(form.errors["intended_workspace_type"]), 1) - self.assertIn("template", form.errors["intended_workspace_type"][0]) - - def test_invalid_workspace_type_unregistered_type(self): - """Form is invalid if intended_workspace_type is not a registered type.""" - form_data = { - "workspace": self.workspace, - "intended_workspace_type": "foo", - } - form = self.form_class(data=form_data) - self.assertFalse(form.is_valid()) - self.assertEqual(len(form.errors), 1) - self.assertIn("intended_workspace_type", form.errors) - self.assertEqual(len(form.errors["intended_workspace_type"]), 1) - self.assertIn("valid choice", form.errors["intended_workspace_type"][0]) - - def test_form_all_registered_adapters(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 == "template": - pass - else: - form_data = { - "workspace": self.workspace, - "intended_workspace_type": workspace_type, - } - form = self.form_class(data=form_data) - self.assertTrue(form.is_valid()) + self.assertIn("intended_usage", form.errors) + self.assertEqual(len(form.errors["intended_usage"]), 1) + self.assertIn("required", form.errors["intended_usage"][0]) class OpenAccessWorkspaceFormTest(TestCase): From 4aebaf86696c8c5ade9ada46d72b65259c099dd5 Mon Sep 17 00:00:00 2001 From: Adrienne Stilp Date: Fri, 22 Mar 2024 16:25:19 -0700 Subject: [PATCH 3/3] Update views for TemplateWorkspace intended_usage change --- primed/miscellaneous_workspaces/tests/test_views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/primed/miscellaneous_workspaces/tests/test_views.py b/primed/miscellaneous_workspaces/tests/test_views.py index ed75975b..6e8d5916 100644 --- a/primed/miscellaneous_workspaces/tests/test_views.py +++ b/primed/miscellaneous_workspaces/tests/test_views.py @@ -734,7 +734,7 @@ def test_creates_workspace(self): "workspacedata-INITIAL_FORMS": 0, "workspacedata-MIN_NUM_FORMS": 1, "workspacedata-MAX_NUM_FORMS": 1, - "workspacedata-0-intended_workspace_type": "resource", + "workspacedata-0-intended_usage": "Test usage", }, ) self.assertEqual(response.status_code, 302) @@ -744,7 +744,7 @@ def test_creates_workspace(self): self.assertEqual(models.TemplateWorkspace.objects.count(), 1) new_workspace_data = models.TemplateWorkspace.objects.latest("pk") self.assertEqual(new_workspace_data.workspace, new_workspace) - self.assertEqual(new_workspace_data.intended_workspace_type, "resource") + self.assertEqual(new_workspace_data.intended_usage, "Test usage") class TemplateWorkspaceImportTest(AnVILAPIMockTestMixin, TestCase): @@ -860,7 +860,7 @@ def test_creates_workspace(self): "workspacedata-INITIAL_FORMS": 0, "workspacedata-MIN_NUM_FORMS": 1, "workspacedata-MAX_NUM_FORMS": 1, - "workspacedata-0-intended_workspace_type": "resource", + "workspacedata-0-intended_usage": "Test usage", }, ) self.assertEqual(response.status_code, 302) @@ -870,7 +870,7 @@ def test_creates_workspace(self): self.assertEqual(models.TemplateWorkspace.objects.count(), 1) new_workspace_data = models.TemplateWorkspace.objects.latest("pk") self.assertEqual(new_workspace_data.workspace, new_workspace) - self.assertEqual(new_workspace_data.intended_workspace_type, "resource") + self.assertEqual(new_workspace_data.intended_usage, "Test usage") class OpenAccessWorkspaceDetailTest(TestCase):