From 8ed42192743c304d1a834f7bff2102a9efde9913 Mon Sep 17 00:00:00 2001 From: Antonio Rodriguez Date: Thu, 20 Mar 2025 20:28:10 +0100 Subject: [PATCH 1/5] Include owned resources on user home. Only show owner or access granted resources on resources listview. --- coldfront/core/portal/views.py | 12 +++++++----- coldfront/core/resource/views.py | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/coldfront/core/portal/views.py b/coldfront/core/portal/views.py index 332b24fc4f..542e523d93 100644 --- a/coldfront/core/portal/views.py +++ b/coldfront/core/portal/views.py @@ -15,7 +15,7 @@ ) from coldfront.core.project.models import Project from coldfront.core.publication.models import Publication -from coldfront.core.resource.models import Resource +from coldfront.core.resource.models import Resource, ResourceAttribute from coldfront.config.env import ENV from coldfront.core.department.models import Department, DepartmentMember from coldfront.core.utils.common import import_from_settings @@ -82,10 +82,12 @@ def home(request): department_list = Department.objects.filter( id__in=user_depts.values_list('organization_id') ) - - resource_list = Resource.objects.filter( - allowed_users=request.user) - + project_title_list = [project.title for project in project_list] + owned_resources = [attribute.resource.pk for attribute in ResourceAttribute.objects.filter( + resource_attribute_type__name='Owner', + value__in=project_title_list + )] + resource_list = Resource.objects.filter(Q(allowed_users=request.user) | Q(pk__in=owned_resources)).distinct() context['resource_list'] = resource_list context['department_list'] = department_list context['project_list'] = project_list diff --git a/coldfront/core/resource/views.py b/coldfront/core/resource/views.py index 58c9efe9e5..a7be1ed475 100644 --- a/coldfront/core/resource/views.py +++ b/coldfront/core/resource/views.py @@ -32,7 +32,7 @@ from coldfront.plugins.slurm.utils import SlurmError -from coldfront.core.project.models import ProjectUser +from coldfront.core.project.models import ProjectUser, Project logger = logging.getLogger(__name__) @@ -297,6 +297,14 @@ def get_queryset(self): order_by = self.return_order() resource_search_form = ResourceSearchForm(self.request.GET) + project_list = Project.objects.filter( + Q(status__name__in=['New', 'Active', ]) & ( + Q(pi=self.request.user) | ( + Q(projectuser__user=self.request.user) + & Q(projectuser__status__name='Active') + ) + ) + ).distinct().order_by('-created') if order_by == 'name': direction = self.request.GET.get('direction') @@ -357,7 +365,12 @@ def get_queryset(self): Q(resourceattribute__resource_attribute_type__name='Vendor') & Q(resourceattribute__value=data.get('vendor')) ) - return resources.distinct() + project_title_list = [project.title for project in project_list] + owned_resources = [attribute.resource.pk for attribute in ResourceAttribute.objects.filter( + resource_attribute_type__name='Owner', + value__in=project_title_list + )] + return resources.filter(Q(allowed_users=self.request.user) | Q(pk__in=owned_resources)).distinct() def get_context_data(self, **kwargs): context = super().get_context_data( From 1f0627e5c46d6e50ab6cc31baf9769017c1e33cf Mon Sep 17 00:00:00 2001 From: Antonio Rodriguez Date: Wed, 26 Mar 2025 17:52:37 +0100 Subject: [PATCH 2/5] Only excluded not owned Compute Nodes from resource listview --- coldfront/core/resource/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/coldfront/core/resource/views.py b/coldfront/core/resource/views.py index a7be1ed475..a5f2c0309d 100644 --- a/coldfront/core/resource/views.py +++ b/coldfront/core/resource/views.py @@ -366,11 +366,12 @@ def get_queryset(self): Q(resourceattribute__value=data.get('vendor')) ) project_title_list = [project.title for project in project_list] - owned_resources = [attribute.resource.pk for attribute in ResourceAttribute.objects.filter( + not_owned_compute_nodes = [attribute.resource.pk for attribute in ResourceAttribute.objects.filter( resource_attribute_type__name='Owner', - value__in=project_title_list - )] - return resources.filter(Q(allowed_users=self.request.user) | Q(pk__in=owned_resources)).distinct() + resource__resource_type__name='Compute Node' + ).exclude(value__in=project_title_list)] + return resources.exclude(pk__in=not_owned_compute_nodes).distinct() + def get_context_data(self, **kwargs): context = super().get_context_data( From fd8fa5d63f44b550e696df26b9fd5fe58ef8baca Mon Sep 17 00:00:00 2001 From: Antonio Rodriguez Date: Wed, 26 Mar 2025 19:01:17 +0100 Subject: [PATCH 3/5] Add listview for retired resources, prevent modifications for retired resources --- .../templates/resource_archived_list.html | 63 +++++++++++ .../resource/templates/resource_detail.html | 6 +- .../resource/templates/resource_list.html | 10 ++ coldfront/core/resource/urls.py | 1 + coldfront/core/resource/views.py | 100 +++++++++++++++++- 5 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 coldfront/core/resource/templates/resource_archived_list.html diff --git a/coldfront/core/resource/templates/resource_archived_list.html b/coldfront/core/resource/templates/resource_archived_list.html new file mode 100644 index 0000000000..d99996a91d --- /dev/null +++ b/coldfront/core/resource/templates/resource_archived_list.html @@ -0,0 +1,63 @@ +{% extends "list_view.html" %} + +{% block title %} +Project List +{% endblock %} + +{% block page_title%}Archived Resources{% endblock %} + +{% block presearch %} + +{% endblock %} + + +{% block list_title %}Resource{{count|pluralize}}: {{count}}{% endblock %} + +{% block table_contents %} + + + + ID + Sort ID asc + Sort ID desc + + + Resource Name + Sort Resource Name asc + Sort Resource Name desc + + + Parent Resource + Sort Parent Resource asc + Sort Parent Resource desc + + + Resource Type + Sort Resource Type asc + Sort Resource Type desc + + + + + {% for resource in item_list %} + + {{ resource.id }} + {{ resource }} + {{ resource.parent_resource }} + {{ resource.resource_type.name }} + + {% endfor %} + +{% endblock %} + +{% block activelink %} +$("#navbar-project-menu").addClass("active"); +$("#navbar-resource").addClass("active"); + +{% endblock %} diff --git a/coldfront/core/resource/templates/resource_detail.html b/coldfront/core/resource/templates/resource_detail.html index 99a0fe9f86..c99304461e 100644 --- a/coldfront/core/resource/templates/resource_detail.html +++ b/coldfront/core/resource/templates/resource_detail.html @@ -12,7 +12,11 @@ {% block content %} - +{% if resource.is_available == False %} + +{% endif %}

Resource Detail


diff --git a/coldfront/core/resource/templates/resource_list.html b/coldfront/core/resource/templates/resource_list.html index da74e6f96b..e5cbc78bc1 100644 --- a/coldfront/core/resource/templates/resource_list.html +++ b/coldfront/core/resource/templates/resource_list.html @@ -3,6 +3,15 @@ {% block title %}Resource List{% endblock %} {% block page_title %}Resources{% endblock %} +{% block presearch %} + +{% endblock %} {% block list_title %}Resource{{count|pluralize}}: {{count}}{% endblock %} @@ -46,4 +55,5 @@ {% block activelink %} $("#navbar-project-menu").addClass("active"); $("#navbar-resource").addClass("active"); + {% endblock %} diff --git a/coldfront/core/resource/urls.py b/coldfront/core/resource/urls.py index afb501f813..30ec4fad7e 100644 --- a/coldfront/core/resource/urls.py +++ b/coldfront/core/resource/urls.py @@ -5,6 +5,7 @@ urlpatterns = [ path('', resource_views.ResourceListView.as_view(), name='resource-list'), + path('archived/', resource_views.ResourceArchivedListView.as_view(), name='resource-archived-list'), path('/', resource_views.ResourceDetailView.as_view(), name='resource-detail'), path('/resourceattribute/add', diff --git a/coldfront/core/resource/views.py b/coldfront/core/resource/views.py index a5f2c0309d..36ba27096d 100644 --- a/coldfront/core/resource/views.py +++ b/coldfront/core/resource/views.py @@ -33,6 +33,7 @@ from coldfront.plugins.slurm.utils import SlurmError from coldfront.core.project.models import ProjectUser, Project +from pandas.io.clipboard import is_available logger = logging.getLogger(__name__) @@ -188,6 +189,18 @@ def test_func(self): messages.error( self.request, 'You do not have permission to add resource attributes.') + def dispatch(self, request, *args, **kwargs): + resource_obj = get_object_or_404(Resource, pk=self.kwargs.get('pk')) + err = None + if resource_obj.is_available is False: + err = 'You cannot add resource attributes to retired allocations.' + if err: + messages.error(request, err) + return HttpResponseRedirect( + reverse('resource-detail', kwargs={'pk': resource_obj.pk}) + ) + return super().dispatch(request, *args, **kwargs) + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) pk = self.kwargs.get('pk') @@ -222,6 +235,18 @@ def test_func(self): messages.error( self.request, 'You do not have permission to delete resource attributes.') + def dispatch(self, request, *args, **kwargs): + resource_obj = get_object_or_404(Resource, pk=self.kwargs.get('pk')) + err = None + if resource_obj.is_available is False: + err = 'You cannot delete resource attributes from retired allocations.' + if err: + messages.error(request, err) + return HttpResponseRedirect( + reverse('resource-detail', kwargs={'pk': resource_obj.pk}) + ) + return super().dispatch(request, *args, **kwargs) + def get(self, request, *args, **kwargs): pk = self.kwargs.get('pk') resource_obj = get_object_or_404(Resource, pk=pk) @@ -370,15 +395,86 @@ def get_queryset(self): resource_attribute_type__name='Owner', resource__resource_type__name='Compute Node' ).exclude(value__in=project_title_list)] + if self.request.user.is_superuser: + return resources.distinct() return resources.exclude(pk__in=not_owned_compute_nodes).distinct() - def get_context_data(self, **kwargs): context = super().get_context_data( SearchFormClass=ResourceSearchForm, **kwargs) return context +class ResourceArchivedListView(ResourceListView): + template_name = 'resource_archived_list.html' + + def get_queryset(self): + + order_by = self.return_order() + resource_search_form = ResourceSearchForm(self.request.GET) + + if order_by == 'name': + direction = self.request.GET.get('direction') + if direction == 'asc': + resources = Resource.objects.all().order_by(Lower('name')) + elif direction == 'des': + resources = (Resource.objects.all().order_by(Lower('name')).reverse()) + else: + resources = Resource.objects.all().order_by(order_by) + else: + resources = Resource.objects.all().order_by(order_by) + if resource_search_form.is_valid(): + data = resource_search_form.cleaned_data + + if data.get('show_allocatable_resources'): + resources = resources.filter(is_allocatable=True) + if data.get('resource_name'): + resources = resources.filter( + name__icontains=data.get('resource_name') + ) + if data.get('resource_type'): + resources = resources.filter( + resource_type=data.get('resource_type') + ) + + if data.get('model'): + resources = resources.filter( + Q(resourceattribute__resource_attribute_type__name='Model') & + Q(resourceattribute__value=data.get('model')) + ) + if data.get('serialNumber'): + resources = resources.filter( + Q(resourceattribute__resource_attribute_type__name='SerialNumber') & + Q(resourceattribute__value=data.get('serialNumber')) + ) + if data.get('installDate'): + resources = resources.filter( + Q(resourceattribute__resource_attribute_type__name='InstallDate') & + Q(resourceattribute__value=data.get('installDate').strftime('%m/%d/%Y')) + ) + if data.get('serviceStart'): + resources = resources.filter( + Q(resourceattribute__resource_attribute_type_name='ServiceStart') & + Q(resourceattribute__value=data.get('serviceStart').strftime('%m/%d/%Y')) + ) + if data.get('serviceEnd'): + resources = resources.filter( + Q(resourceattribute__resource_attribute_type__name='ServiceEnd') & + Q(resourceattribute__value=data.get('serviceEnd').strftime('%m/%d/%Y')) + ) + if data.get('warrantyExpirationDate'): + resources = resources.filter( + Q(resourceattribute__resource_attribute_type__name='WarrantyExpirationDate') & + Q(resourceattribute__value=data.get('warrantyExpirationDate').strftime('%m/%d/%Y')) + ) + if data.get('vendor'): + resources = resources.filter( + Q(resourceattribute__resource_attribute_type__name='Vendor') & + Q(resourceattribute__value=data.get('vendor')) + ) + return resources.exclude(is_available=True).distinct() + + class ResourceAllocationsEditView(LoginRequiredMixin, UserPassesTestMixin, TemplateView): template_name = 'resource_allocations_edit.html' @@ -396,6 +492,8 @@ def dispatch(self, request, *args, **kwargs): err = None if 'Storage' in resource_obj.resource_type.name: err = 'You cannot bulk-edit storage allocations.' + if resource_obj.is_available is False: + err = 'You cannot edit retired allocations.' if err: messages.error(request, err) return HttpResponseRedirect( From 52cd52caa1a5a06c694a1b392bb07ce1eb7f3ae4 Mon Sep 17 00:00:00 2001 From: Antonio Rodriguez Date: Wed, 9 Apr 2025 16:20:37 +0200 Subject: [PATCH 4/5] Tests --- coldfront/core/resource/tests.py | 65 ++++++++++++- coldfront/core/test_helpers/factories.py | 24 +++++ .../test_data/test_fixtures/resource.json | 97 +++++++++++++++++++ 3 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 coldfront/core/test_helpers/test_data/test_fixtures/resource.json diff --git a/coldfront/core/resource/tests.py b/coldfront/core/resource/tests.py index b364d45a4a..8bc1219aef 100644 --- a/coldfront/core/resource/tests.py +++ b/coldfront/core/resource/tests.py @@ -1,12 +1,20 @@ +from boto3 import resource +from django.db.models import Q from django.test import TestCase from coldfront.core.test_helpers import utils -from coldfront.core.test_helpers.factories import setup_models +from coldfront.core.test_helpers.factories import setup_models, AttributeTypeFactory, ProjectFactory, ResourceFactory, ResourceTypeFactory, ResourceAttributeTypeFactory, ResourceAttributeFactory +from coldfront.core.project.models import Project +from coldfront.core.resource.models import AttributeType, ResourceType UTIL_FIXTURES = [ "coldfront/core/test_helpers/test_data/test_fixtures/ifx.json", ] +RESOURCE_FIXTURES = [ + "coldfront/core/test_helpers/test_data/test_fixtures/resource.json", +] + BACKEND = "django.contrib.auth.backends.ModelBackend" @@ -27,11 +35,63 @@ def resource_access_tstbase(self, url): utils.test_logged_out_redirect_to_login(self, url) utils.test_user_can_access(self, self.admin_user, url) # admin can access + class ResourceListViewTest(ResourceViewBaseTest): """Tests for ResourceListView""" + fixtures = RESOURCE_FIXTURES def setUp(self): - self.client.force_login(self.admin_user, backend=BACKEND) + self.client.force_login(self.pi_user, backend=BACKEND) + self.url = f'/resource/' + + def test_only_user_managed_compute_nodes_show(self): + ProjectFactory(pi=self.pi_user, title="managed_lab") + ProjectFactory(pi=self.admin_user, title="admin_lab") + text_attribute_type = AttributeType.objects.get(name="Text") + managed_resource = ResourceFactory(name="managed_lab", resource_type__name='Compute Node') + admin_resource = ResourceFactory(name="admin_lab", resource_type__name='Compute Node') + owner_resourcer_attr_type = ResourceAttributeTypeFactory(name="Owner", attribute_type=text_attribute_type) + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="managed_lab", resource=managed_resource) + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="admin_lab", resource=admin_resource) + utils.page_contains_for_user(self, self.pi_user, self.url, 'managed_lab') + utils.page_does_not_contain_for_user(self, self.pi_user, self.url, 'admin_lab') + utils.page_contains_for_user(self, self.admin_user, self.url, 'admin_lab') + utils.page_contains_for_user(self, self.admin_user, self.url, 'managed_lab') + + def test_retired_resources_filter_shows(self): + utils.page_contains_for_user(self, self.pi_user, self.url, 'View retired resources') + utils.page_contains_for_user(self, self.admin_user, self.url, 'View retired resources') + + +class ResourceArchivedListViewTest(ResourceViewBaseTest): + """Tests for ResourceArchivedListView""" + fixtures = RESOURCE_FIXTURES + + def setUp(self): + self.client.force_login(self.pi_user, backend=BACKEND) + self.url = f'/resource/archived/' + + def test_archive_resources_show(self): + ResourceFactory(name="archived_resource", resource_type__name='Compute Node', is_available=False) + ResourceFactory(name="active_resource", resource_type__name='Compute Node') + utils.page_contains_for_user(self, self.pi_user, self.url, 'archived_resource') + utils.page_does_not_contain_for_user(self, self.pi_user, self.url, 'active_resource') + utils.page_contains_for_user(self, self.admin_user, self.url, 'archived_resource') + utils.page_does_not_contain_for_user(self, self.admin_user, self.url, 'active_resource') + + def test_can_filter_by_name(self): + AttributeType.objects.get(name="Text") + ResourceFactory(name="archived_resource", resource_type__name='Compute Node', is_available=False) + ResourceFactory(name="archived_resource2", resource_type__name='Compute Node', is_available=False) + ResourceFactory(name="active_resource", resource_type__name='Compute Node') + search_url = f'{self.url}?resource_name=archived_resource' + utils.page_contains_for_user(self, self.pi_user, search_url, 'archived_resource') + utils.page_does_not_contain_for_user(self, self.pi_user, search_url, 'archived_resource2') + utils.page_does_not_contain_for_user(self, self.pi_user, search_url, 'active_resource') + search_url = f'{self.url}?resource_name=archived_resource2' + utils.page_contains_for_user(self, self.pi_user, search_url, 'archived_resource2') + utils.page_does_not_contain_for_user(self, self.pi_user, search_url, 'archived_resource') + utils.page_does_not_contain_for_user(self, self.pi_user, search_url, 'active_resource') class ClusterResourceDetailViewTest(ResourceViewBaseTest): @@ -146,6 +206,7 @@ def test_resource_attribute_create_access(self): utils.test_user_cannot_access(self, self.resource_allowed_user, self.url) utils.test_user_cannot_access(self, self.pi_user, self.url) + class ClusterResourceAttributeDeleteViewTest(ResourceViewBaseTest): """Tests for ResourceAttributeDeleteView""" diff --git a/coldfront/core/test_helpers/factories.py b/coldfront/core/test_helpers/factories.py index 98c54928d6..7d922ead9c 100644 --- a/coldfront/core/test_helpers/factories.py +++ b/coldfront/core/test_helpers/factories.py @@ -36,6 +36,8 @@ from coldfront.core.grant.models import GrantFundingAgency, GrantStatusChoice from coldfront.core.publication.models import PublicationSource +from coldfront.core.allocation.models import AttributeType +from coldfront.core.resource.models import ResourceAttribute, ResourceAttributeType ### Default values and Faker provider setup ### @@ -212,7 +214,29 @@ class Meta: description = factory.Faker('sentence') resource_type = SubFactory(ResourceTypeFactory) +class AttributeTypeFactory(DjangoModelFactory): + class Meta: + model = AttributeType + + name = factory.Faker("word") + +class ResourceAttributeTypeFactory(DjangoModelFactory): + class Meta: + model = ResourceAttributeType + + attribute_type = factory.SubFactory(AttributeTypeFactory) + name = factory.Faker("word") + is_required = factory.Faker("boolean") + is_unique_per_resource = factory.Faker("boolean") + is_value_unique = factory.Faker("boolean") + +class ResourceAttributeFactory(DjangoModelFactory): + class Meta: + model = ResourceAttribute + django_get_or_create = ('resource', 'resource_attribute_type') + value = 'admin_lab' + resource = factory.SubFactory(ResourceFactory) ### Allocation factories ### diff --git a/coldfront/core/test_helpers/test_data/test_fixtures/resource.json b/coldfront/core/test_helpers/test_data/test_fixtures/resource.json new file mode 100644 index 0000000000..23ad32b7fa --- /dev/null +++ b/coldfront/core/test_helpers/test_data/test_fixtures/resource.json @@ -0,0 +1,97 @@ +[ + + { + "model": "resource.attributetype", + "pk": 1, + "fields": { + "created": "2024-10-10T01:18:54.259Z", + "modified": "2024-10-10T01:18:54.259Z", + "name": "Active/Inactive" + } + }, + { + "model": "resource.attributetype", + "pk": 2, + "fields": { + "created": "2024-10-10T01:18:54.266Z", + "modified": "2024-10-10T01:18:54.266Z", + "name": "Date" + } + }, + { + "model": "resource.attributetype", + "pk": 3, + "fields": { + "created": "2024-10-10T01:18:54.277Z", + "modified": "2024-10-10T01:18:54.277Z", + "name": "Int" + } + }, + + { + "model": "resource.attributetype", + "pk": 5, + "fields": { + "created": "2024-10-10T01:18:54.290Z", + "modified": "2024-10-10T01:18:54.290Z", + "name": "Text" + } + }, + { + "model": "resource.attributetype", + "pk": 7, + "fields": { + "created": "2024-10-10T01:18:54.300Z", + "modified": "2024-10-10T01:18:54.300Z", + "name": "Attribute Expanded Text" + } + }, + { + "model": "resource.attributetype", + "pk": 8, + "fields": { + "created": "2024-10-10T01:18:54.305Z", + "modified": "2024-10-10T01:18:54.305Z", + "name": "Float" + } + }, + { + "model": "resource.resourceattributetype", + "pk": 10, + "fields": { + "created": "2024-10-11T00:26:36.739Z", + "modified": "2024-10-11T00:26:36.739Z", + "attribute_type": 5, + "name": "slurm_cluster", + "is_required": false, + "is_unique_per_resource": false, + "is_value_unique": false + } + }, + { + "model": "resource.resourceattributetype", + "pk": 15, + "fields": { + "created": "2025-03-21T21:50:07.174Z", + "modified": "2025-03-21T21:50:07.174Z", + "attribute_type": 5, + "name": "Owner", + "is_required": false, + "is_unique_per_resource": false, + "is_value_unique": false + } + }, + { + "model": "resource.resourceattributetype", + "pk": 16, + "fields": { + "created": "2025-03-21T21:50:07.177Z", + "modified": "2025-03-21T21:50:07.177Z", + "attribute_type": 2, + "name": "ServiceEnd", + "is_required": false, + "is_unique_per_resource": false, + "is_value_unique": false + } + } +] From c25a0a4fc405ced4ab5b0c241e0e16700aa3d0ee Mon Sep 17 00:00:00 2001 From: Antonio Rodriguez Date: Wed, 9 Apr 2025 17:47:19 +0200 Subject: [PATCH 5/5] Filter our archived resources from user home page managed resources. Add more tests --- coldfront/core/portal/tests.py | 47 +++++++++++++++++++++++++++++++- coldfront/core/portal/views.py | 3 +- coldfront/core/resource/tests.py | 9 ++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/coldfront/core/portal/tests.py b/coldfront/core/portal/tests.py index 69796f91d0..0cb1db8076 100644 --- a/coldfront/core/portal/tests.py +++ b/coldfront/core/portal/tests.py @@ -2,9 +2,14 @@ from coldfront.core.test_helpers import utils from coldfront.core.test_helpers.factories import setup_models from coldfront.core.allocation.models import AllocationChangeRequest, AllocationChangeStatusChoice +from coldfront.core.test_helpers.factories import setup_models, AttributeTypeFactory, ProjectFactory, ResourceFactory, ResourceTypeFactory, ResourceAttributeTypeFactory, ResourceAttributeFactory +from coldfront.core.project.models import Project +from coldfront.core.resource.models import AttributeType, ResourceType UTIL_FIXTURES = ['coldfront/core/test_helpers/test_data/test_fixtures/ifx.json'] - +RESOURCE_FIXTURES = [ + "coldfront/core/test_helpers/test_data/test_fixtures/resource.json", +] class PortalViewTest(TestCase): """Base class for portal view tests @@ -29,6 +34,7 @@ def test_center_summary(self): class HomePageTest(PortalViewTest): + fixtures = RESOURCE_FIXTURES def test_pi_home_page(self): """check that the pi home page displays properly with the existing database @@ -85,3 +91,42 @@ def test_home_page_projects_display(self): # allocationuser not belonging to project cannot see project response = utils.login_and_get_page(self.client, self.nonproj_allocationuser, '') self.assertEqual(response.context['project_list'].count(), 0) + + def test_home_page_managed_resources_display(self): + """check that managed resources display properly on the home page + """ + ProjectFactory(pi=self.pi_user, title="managed_lab") + ProjectFactory(pi=self.admin_user, title="admin_lab") + text_attribute_type = AttributeType.objects.get(name="Text") + managed_resource = ResourceFactory(name="managed_lab", resource_type__name='Compute Node') + managed_resource2 = ResourceFactory(name="managed_lab2", resource_type__name='Compute Node') + admin_resource = ResourceFactory(name="admin_lab", resource_type__name='Compute Node') + owner_resourcer_attr_type = ResourceAttributeTypeFactory(name="Owner", attribute_type=text_attribute_type) + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="managed_lab", + resource=managed_resource) + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="managed_lab", + resource=managed_resource2) + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="admin_lab", + resource=admin_resource) + utils.page_contains_for_user(self, self.pi_user, '', 'Managed Resources') + utils.page_contains_for_user(self, self.admin_user, '', 'Managed Resources') + utils.page_contains_for_user(self, self.pi_user, '', 'managed_lab') + utils.page_contains_for_user(self, self.pi_user, '', 'managed_lab2') + utils.page_does_not_contain_for_user(self, self.pi_user, '', 'admin_lab') + utils.page_contains_for_user(self, self.admin_user, '', 'admin_lab') + utils.page_does_not_contain_for_user(self, self.admin_user, '', 'managed_lab') + utils.page_does_not_contain_for_user(self, self.admin_user, '', 'managed_lab2') + + def test_home_page_archive_resources_dont_show(self): + ProjectFactory(pi=self.pi_user, title="managed_lab") + text_attribute_type = AttributeType.objects.get(name="Text") + owner_resourcer_attr_type = ResourceAttributeTypeFactory(name="Owner", attribute_type=text_attribute_type) + archived_resource = ResourceFactory(name="archived_resource", resource_type__name='Compute Node', is_available=False) + archived_resource2 = ResourceFactory(name="archived_resource2", resource_type__name='Compute Node', is_available=False) + active_resource = ResourceFactory(name="active_resource", resource_type__name='Compute Node') + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="managed_lab", resource=archived_resource) + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="managed_lab", resource=archived_resource2) + ResourceAttributeFactory(resource_attribute_type=owner_resourcer_attr_type, value="managed_lab", resource=active_resource) + utils.page_contains_for_user(self, self.pi_user, '', 'active_resource') + utils.page_does_not_contain_for_user(self, self.pi_user, '', 'archived_resource') + utils.page_does_not_contain_for_user(self, self.pi_user, '', 'archived_resource2') diff --git a/coldfront/core/portal/views.py b/coldfront/core/portal/views.py index 8bf0a2de82..c847753fdd 100644 --- a/coldfront/core/portal/views.py +++ b/coldfront/core/portal/views.py @@ -19,6 +19,7 @@ from coldfront.config.env import ENV from coldfront.core.department.models import Department, DepartmentMember from coldfront.core.utils.common import import_from_settings +from pandas.io.clipboard import is_available if ENV.bool('PLUGIN_SFTOCF', default=False): from coldfront.plugins.sftocf.utils import StarFishRedash, STARFISH_SERVER @@ -87,7 +88,7 @@ def home(request): resource_attribute_type__name='Owner', value__in=project_title_list )] - resource_list = Resource.objects.filter(Q(allowed_users=request.user) | Q(pk__in=owned_resources)).distinct() + resource_list = Resource.objects.filter(Q(allowed_users=request.user) | Q(pk__in=owned_resources)).filter(is_available=True).distinct() context['resource_list'] = resource_list context['department_list'] = department_list context['project_list'] = project_list diff --git a/coldfront/core/resource/tests.py b/coldfront/core/resource/tests.py index 8bc1219aef..c6e291e3b5 100644 --- a/coldfront/core/resource/tests.py +++ b/coldfront/core/resource/tests.py @@ -62,6 +62,15 @@ def test_retired_resources_filter_shows(self): utils.page_contains_for_user(self, self.pi_user, self.url, 'View retired resources') utils.page_contains_for_user(self, self.admin_user, self.url, 'View retired resources') + def test_archive_resources_dont_show(self): + ResourceFactory(name="archived_resource", resource_type__name='Compute Node', is_available=False) + ResourceFactory(name="archived_resource2", resource_type__name='Compute Node', is_available=False) + ResourceFactory(name="active_resource", resource_type__name='Compute Node') + utils.page_contains_for_user(self, self.pi_user, self.url, 'active_resource') + utils.page_does_not_contain_for_user(self, self.pi_user, self.url, 'archived_resource') + utils.page_does_not_contain_for_user(self, self.pi_user, self.url, 'archived_resource2') + + class ResourceArchivedListViewTest(ResourceViewBaseTest): """Tests for ResourceArchivedListView"""