From 1df24478dd98551b77e58ad21d6a671337ef3d53 Mon Sep 17 00:00:00 2001 From: Hamza Kundi Date: Tue, 19 Mar 2024 11:39:52 -0700 Subject: [PATCH 1/6] added service units field to project view --- .../project/templates/project/project_list.html | 14 +++++++++++++- coldfront/core/project/views.py | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/coldfront/core/project/templates/project/project_list.html b/coldfront/core/project/templates/project/project_list.html index 0a6fa68af..6263fb73b 100644 --- a/coldfront/core/project/templates/project/project_list.html +++ b/coldfront/core/project/templates/project/project_list.html @@ -118,10 +118,21 @@

{{ PROGRAM_NAME_SHORT }} Cluster Projects


+ + Service Units + + Sort by Ascending Service Units + + + + Sort by Descending Service Units + + + - {% for project in project_list %} + {% for project, compute_allocation_information in zipped %} @@ -139,6 +150,7 @@

{{ PROGRAM_NAME_SHORT }} Cluster Projects


{{ project.cluster_name|upper }} {{ project.status.name }} + {{ compute_allocation_information }} {% endfor %} diff --git a/coldfront/core/project/views.py b/coldfront/core/project/views.py index 17fdb7533..5810749b0 100644 --- a/coldfront/core/project/views.py +++ b/coldfront/core/project/views.py @@ -369,13 +369,13 @@ def get_queryset(self): projects = projects.filter(cluster_name__icontains=data.get('cluster_name')) else: - projects = Project.objects.prefetch_related('field_of_science', 'status',).filter( + projects = Project.objects.prefetch_related('field_of_science', 'status', 'allocation').filter( Q(status__name__in=['New', 'Active', 'Inactive', ]) & Q(projectuser__user=self.request.user) & Q(projectuser__status__name__in=['Active', 'Pending - Remove']) ).order_by(order_by) projects = annotate_queryset_with_cluster_name(projects) - + return projects.distinct() def get_context_data(self, **kwargs): @@ -443,6 +443,16 @@ def get_context_data(self, **kwargs): context['user_agreement_signed'] = \ access_agreement_signed(self.request.user) + compute_allocation_information = [] + for project in project_list: + try: + information = get_project_compute_allocation(project).get_information + compute_allocation_information.append(information if information else 'N/A') + except Allocation.DoesNotExist: + compute_allocation_information.append('N/A') + + context['compute_allocation_information'] = compute_allocation_information + context['zipped'] = zip(project_list, compute_allocation_information) return context From 2102b64dc391bcf7896b61a673e6b7dc2b465a80 Mon Sep 17 00:00:00 2001 From: Hamza Kundi Date: Tue, 2 Apr 2024 09:49:09 -0700 Subject: [PATCH 2/6] added service units field to home page projects --- .../core/portal/templates/portal/authorized_home.html | 4 ++++ coldfront/core/portal/views.py | 9 ++++++++- coldfront/core/project/views.py | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/coldfront/core/portal/templates/portal/authorized_home.html b/coldfront/core/portal/templates/portal/authorized_home.html index 1c3a5ea5c..487d3a647 100644 --- a/coldfront/core/portal/templates/portal/authorized_home.html +++ b/coldfront/core/portal/templates/portal/authorized_home.html @@ -142,6 +142,9 @@

My {{ PROGRAM_NAME_SHORT }} Cluster Projects »

Access to Project on Cluster + + Service Units + @@ -172,6 +175,7 @@

My {{ PROGRAM_NAME_SHORT }} Cluster Projects »

No Access {% endif %} + {{ project.compute_allocation_information }} {% endfor %} diff --git a/coldfront/core/portal/views.py b/coldfront/core/portal/views.py index 54591915a..82cc17442 100644 --- a/coldfront/core/portal/views.py +++ b/coldfront/core/portal/views.py @@ -11,7 +11,8 @@ from coldfront.core.allocation.models import (Allocation, AllocationUser, AllocationUserAttribute) -from coldfront.core.allocation.utils import get_project_compute_resource_name +from coldfront.core.allocation.utils import (get_project_compute_allocation, + get_project_compute_resource_name) # from coldfront.core.grant.models import Grant from coldfront.core.portal.utils import (generate_allocations_chart_data, generate_publication_by_year_chart_data, @@ -53,6 +54,12 @@ def home(request): resource_name = get_project_compute_resource_name(project) project.cluster_name = resource_name.replace(' Compute', '') + try: + information = get_project_compute_allocation(project).get_information + information = information[len('Service Units: '):-len('
')] + project.compute_allocation_information = information if information else 'N/A' + except Allocation.DoesNotExist: + project.compute_allocation_information = 'N/A' allocation_list = Allocation.objects.filter( Q(status__name__in=['Active', 'New', 'Renewal Requested', ]) & diff --git a/coldfront/core/project/views.py b/coldfront/core/project/views.py index 5810749b0..9f32ca345 100644 --- a/coldfront/core/project/views.py +++ b/coldfront/core/project/views.py @@ -447,6 +447,7 @@ def get_context_data(self, **kwargs): for project in project_list: try: information = get_project_compute_allocation(project).get_information + information = information[len('Service Units: '):-len('
')] compute_allocation_information.append(information if information else 'N/A') except Allocation.DoesNotExist: compute_allocation_information.append('N/A') From 663583d535dfd9d7631cf69980eddda66fe8d552 Mon Sep 17 00:00:00 2001 From: Hamza Kundi Date: Tue, 30 Apr 2024 10:07:36 -0700 Subject: [PATCH 3/6] removed buggy sort, standardized su fetching implementation --- .../project/templates/project/project_list.html | 14 +++----------- coldfront/core/project/views.py | 10 +++++----- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/coldfront/core/project/templates/project/project_list.html b/coldfront/core/project/templates/project/project_list.html index 6263fb73b..7f1d554cc 100644 --- a/coldfront/core/project/templates/project/project_list.html +++ b/coldfront/core/project/templates/project/project_list.html @@ -120,19 +120,11 @@

{{ PROGRAM_NAME_SHORT }} Cluster Projects


Service Units -
- Sort by Ascending Service Units - - - - Sort by Descending Service Units - - - {% for project, compute_allocation_information in zipped %} + {% for project in project_list %} @@ -148,9 +140,9 @@

{{ PROGRAM_NAME_SHORT }} Cluster Projects


{{ project.title }} {{ project.cluster_name|upper }} - + {{ project.field_of_science.description }} {{ project.status.name }} - {{ compute_allocation_information }} + {{ project.compute_allocation_information }} {% endfor %} diff --git a/coldfront/core/project/views.py b/coldfront/core/project/views.py index 9f32ca345..530d7fed1 100644 --- a/coldfront/core/project/views.py +++ b/coldfront/core/project/views.py @@ -443,17 +443,17 @@ def get_context_data(self, **kwargs): context['user_agreement_signed'] = \ access_agreement_signed(self.request.user) - compute_allocation_information = [] for project in project_list: + resource_name = get_project_compute_resource_name(project) + project.cluster_name = resource_name.replace(' Compute', '') try: information = get_project_compute_allocation(project).get_information information = information[len('Service Units: '):-len('
')] - compute_allocation_information.append(information if information else 'N/A') + project.compute_allocation_information = information if information else 'N/A' except Allocation.DoesNotExist: - compute_allocation_information.append('N/A') + project.compute_allocation_information = 'N/A' + context['project_list'] = project_list - context['compute_allocation_information'] = compute_allocation_information - context['zipped'] = zip(project_list, compute_allocation_information) return context From dc09b0d8793b3099783e338d968f6f41f169c3e2 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Fri, 3 May 2024 13:36:23 -0700 Subject: [PATCH 4/6] Re-comment out FieldOfScience description to avoid table misalignment --- coldfront/core/project/templates/project/project_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coldfront/core/project/templates/project/project_list.html b/coldfront/core/project/templates/project/project_list.html index 7f1d554cc..31537d98a 100644 --- a/coldfront/core/project/templates/project/project_list.html +++ b/coldfront/core/project/templates/project/project_list.html @@ -140,7 +140,7 @@

{{ PROGRAM_NAME_SHORT }} Cluster Projects


{{ project.title }} {{ project.cluster_name|upper }} - {{ project.field_of_science.description }} + {{ project.status.name }} {{ project.compute_allocation_information }} From 9c46de684dc00a979c10d69e77c9928d7fb272a8 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Fri, 3 May 2024 15:13:35 -0700 Subject: [PATCH 5/6] Remove invalid prefetch_related arg; avoid redundant query to get project resource in home view; catch all exceptions --- coldfront/core/portal/views.py | 8 +++++--- coldfront/core/project/views.py | 13 ++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/coldfront/core/portal/views.py b/coldfront/core/portal/views.py index 82cc17442..9a2a747d6 100644 --- a/coldfront/core/portal/views.py +++ b/coldfront/core/portal/views.py @@ -55,10 +55,12 @@ def home(request): resource_name = get_project_compute_resource_name(project) project.cluster_name = resource_name.replace(' Compute', '') try: - information = get_project_compute_allocation(project).get_information + allocation = project.allocation_set.get( + resources__name=resource_name) + information = allocation.get_information information = information[len('Service Units: '):-len('
')] - project.compute_allocation_information = information if information else 'N/A' - except Allocation.DoesNotExist: + project.compute_allocation_information = information or 'N/A' + except Exception: project.compute_allocation_information = 'N/A' allocation_list = Allocation.objects.filter( diff --git a/coldfront/core/project/views.py b/coldfront/core/project/views.py index 530d7fed1..9642623c0 100644 --- a/coldfront/core/project/views.py +++ b/coldfront/core/project/views.py @@ -369,13 +369,13 @@ def get_queryset(self): projects = projects.filter(cluster_name__icontains=data.get('cluster_name')) else: - projects = Project.objects.prefetch_related('field_of_science', 'status', 'allocation').filter( + projects = Project.objects.prefetch_related('field_of_science', 'status',).filter( Q(status__name__in=['New', 'Active', 'Inactive', ]) & Q(projectuser__user=self.request.user) & Q(projectuser__status__name__in=['Active', 'Pending - Remove']) ).order_by(order_by) projects = annotate_queryset_with_cluster_name(projects) - + return projects.distinct() def get_context_data(self, **kwargs): @@ -444,13 +444,12 @@ def get_context_data(self, **kwargs): access_agreement_signed(self.request.user) for project in project_list: - resource_name = get_project_compute_resource_name(project) - project.cluster_name = resource_name.replace(' Compute', '') try: - information = get_project_compute_allocation(project).get_information + information = get_project_compute_allocation( + project).get_information information = information[len('Service Units: '):-len('
')] - project.compute_allocation_information = information if information else 'N/A' - except Allocation.DoesNotExist: + project.compute_allocation_information = information or 'N/A' + except Exception: project.compute_allocation_information = 'N/A' context['project_list'] = project_list From a5c82a2a4897cd3e802a8bb3a26fce150d1e5033 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Tue, 1 Oct 2024 15:44:43 -0700 Subject: [PATCH 6/6] Add function for rendering project compute usage; use it in views --- .../templates/portal/authorized_home.html | 2 +- coldfront/core/portal/views.py | 10 ++--- .../templates/project/project_list.html | 2 +- coldfront/core/project/utils.py | 44 +++++++++++++++++++ coldfront/core/project/views.py | 13 +++--- 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/coldfront/core/portal/templates/portal/authorized_home.html b/coldfront/core/portal/templates/portal/authorized_home.html index 487d3a647..4a0a28f19 100644 --- a/coldfront/core/portal/templates/portal/authorized_home.html +++ b/coldfront/core/portal/templates/portal/authorized_home.html @@ -175,7 +175,7 @@

My {{ PROGRAM_NAME_SHORT }} Cluster Projects »

No Access {% endif %} - {{ project.compute_allocation_information }} + {{ project.rendered_compute_usage }} {% endfor %} diff --git a/coldfront/core/portal/views.py b/coldfront/core/portal/views.py index 9a2a747d6..d0bde07b3 100644 --- a/coldfront/core/portal/views.py +++ b/coldfront/core/portal/views.py @@ -21,6 +21,7 @@ from coldfront.core.project.models import Project, ProjectUserJoinRequest from coldfront.core.project.models import ProjectUserJoinRequest from coldfront.core.project.models import ProjectUserRemovalRequest +from coldfront.core.project.utils import render_project_compute_usage # from coldfront.core.publication.models import Publication @@ -55,13 +56,10 @@ def home(request): resource_name = get_project_compute_resource_name(project) project.cluster_name = resource_name.replace(' Compute', '') try: - allocation = project.allocation_set.get( - resources__name=resource_name) - information = allocation.get_information - information = information[len('Service Units: '):-len('
')] - project.compute_allocation_information = information or 'N/A' + rendered_compute_usage = render_project_compute_usage(project) except Exception: - project.compute_allocation_information = 'N/A' + rendered_compute_usage = 'Unexpected error' + project.rendered_compute_usage = rendered_compute_usage allocation_list = Allocation.objects.filter( Q(status__name__in=['Active', 'New', 'Renewal Requested', ]) & diff --git a/coldfront/core/project/templates/project/project_list.html b/coldfront/core/project/templates/project/project_list.html index 31537d98a..179816c25 100644 --- a/coldfront/core/project/templates/project/project_list.html +++ b/coldfront/core/project/templates/project/project_list.html @@ -142,7 +142,7 @@

{{ PROGRAM_NAME_SHORT }} Cluster Projects


{{ project.cluster_name|upper }} {{ project.status.name }} - {{ project.compute_allocation_information }} + {{ project.rendered_compute_usage }} {% endfor %} diff --git a/coldfront/core/project/utils.py b/coldfront/core/project/utils.py index 886740bc2..fe6186a20 100644 --- a/coldfront/core/project/utils.py +++ b/coldfront/core/project/utils.py @@ -1,3 +1,5 @@ +from decimal import Decimal + from django.db import transaction from flags.state import flag_enabled @@ -42,6 +44,48 @@ def project_join_list_url(): return urljoin(domain, view) +def render_project_compute_usage(project): + """Return a str containing the given Project's usage of its compute + allowance, the total allowance, and the percentage used, rounded to + two decimal places. + + Return 'N/A': + - If the Project's status is not 'Active'. + - If relevant database objects cannot be retrieved. This is done + for simplicity, even though some cases would be unexpected. + + Return 'Failed to compute' if calculations fail. + + Returns: + - str + + Raises: + - AssertionError + """ + assert isinstance(project, Project) + + n_a = 'N/A' + if project.status.name != 'Active': + return n_a + try: + accounting_allocation_objects = get_accounting_allocation_objects( + project) + except Exception: + return n_a + + try: + allowance = Decimal( + accounting_allocation_objects.allocation_attribute.value) + usage = Decimal( + accounting_allocation_objects.allocation_attribute_usage.value) + # Retain two decimal places. + percentage = ( + Decimal(100.00) * usage / allowance).quantize(Decimal('0.00')) + except Exception: + return 'Failed to compute' + return f'{usage}/{allowance} ({percentage} %)' + + def review_project_join_requests_url(project): domain = import_from_settings('CENTER_BASE_URL') view = reverse('project-review-join-requests', kwargs={'pk': project.pk}) diff --git a/coldfront/core/project/views.py b/coldfront/core/project/views.py index 9642623c0..60ebc42f2 100644 --- a/coldfront/core/project/views.py +++ b/coldfront/core/project/views.py @@ -42,8 +42,9 @@ ProjectUserStatusChoice, ProjectUserRemovalRequest, SavioProjectAllocationRequest) -from coldfront.core.project.utils import (annotate_queryset_with_cluster_name, - is_primary_cluster_project) +from coldfront.core.project.utils import annotate_queryset_with_cluster_name +from coldfront.core.project.utils import is_primary_cluster_project +from coldfront.core.project.utils import render_project_compute_usage from coldfront.core.project.utils_.addition_utils import can_project_purchase_service_units from coldfront.core.project.utils_.new_project_user_utils import NewProjectUserRunnerFactory from coldfront.core.project.utils_.new_project_user_utils import NewProjectUserSource @@ -445,12 +446,10 @@ def get_context_data(self, **kwargs): for project in project_list: try: - information = get_project_compute_allocation( - project).get_information - information = information[len('Service Units: '):-len('
')] - project.compute_allocation_information = information or 'N/A' + rendered_compute_usage = render_project_compute_usage(project) except Exception: - project.compute_allocation_information = 'N/A' + rendered_compute_usage = 'Unexpected error' + project.rendered_compute_usage = rendered_compute_usage context['project_list'] = project_list return context