Skip to content

Commit 2e4169e

Browse files
authored
Merge pull request #129 from NYU-RTS/allocation_unique_by_project_and_resource
Allocations should be unique by project & resource
2 parents 2a57a60 + 331678e commit 2e4169e

File tree

3 files changed

+37
-66
lines changed

3 files changed

+37
-66
lines changed

coldfront/core/allocation/test_views.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,32 @@ def test_allocationcreateview_post_zeroquantity(self):
508508
self.assertContains(response, "Allocation requested.")
509509
self.assertEqual(len(self.project.allocation_set.all()), 2)
510510

511+
def test_allocationcreateview_post_duplicate_project_resource_shows_error(self):
512+
"""Posting an allocation for same project+resource should show error and not create a new allocation"""
513+
self.post_data["resource"] = str(self.resource_tandon.pk)
514+
515+
# Create an existing allocation for (project, resource_tandon)
516+
existing = Allocation.objects.create(
517+
project=self.project,
518+
justification="existing",
519+
quantity=1,
520+
status=AllocationStatusChoice.objects.get(name="New"),
521+
)
522+
existing.resources.add(self.resource_tandon)
523+
524+
initial_count = self.project.allocation_set.count()
525+
526+
response = self.client.post(self.url, data=self.post_data, follow=True)
527+
528+
self.assertEqual(response.status_code, 200)
529+
# Should not create a new allocation
530+
self.assertEqual(self.project.allocation_set.count(), initial_count)
531+
532+
# Should show the friendly error message you added in form_valid
533+
self.assertContains(
534+
response,
535+
"An allocation for this project and resource already exists.",
536+
)
511537

512538
class AllocationAddUsersViewTest(AllocationViewBaseTest):
513539
"""Tests for the AllocationAddUsersView"""

coldfront/core/allocation/views.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
import plotly.express as px
8181
from plotly.offline import plot
8282
from django.http import HttpResponse, HttpResponseBadRequest
83+
from django.db import IntegrityError, transaction
8384

8485
ALLOCATION_ENABLE_ALLOCATION_RENEWAL = import_from_settings("ALLOCATION_ENABLE_ALLOCATION_RENEWAL", True)
8586
ALLOCATION_DEFAULT_ALLOCATION_LENGTH = import_from_settings("ALLOCATION_DEFAULT_ALLOCATION_LENGTH", 365)
@@ -614,6 +615,14 @@ def form_valid(self, form):
614615
)
615616
return self.form_invalid(form)
616617

618+
# Prevent duplicate allocation for same (project, resource)
619+
if Allocation.objects.filter(project=project_obj, resources=resource_obj).exists():
620+
form.add_error(
621+
"resource",
622+
"An allocation for this project and resource already exists.",
623+
)
624+
return self.form_invalid(form)
625+
617626
usernames = form_data.get("users")
618627
usernames.append(project_obj.pi.username)
619628
usernames = list(set(usernames))

coldfront/core/utils/management/commands/load_test_data.py

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -269,43 +269,7 @@ def handle(self, *args, **options):
269269
start_date = datetime.datetime.now()
270270
end_date = datetime.datetime.now() + relativedelta(days=365)
271271

272-
# Add PI cluster
273-
allocation_obj, _ = Allocation.objects.get_or_create(
274-
project=project_obj,
275-
status=AllocationStatusChoice.objects.get(name="Active"),
276-
start_date=start_date,
277-
end_date=end_date,
278-
is_changeable=True,
279-
justification="I need access to my nodes.",
280-
)
281-
282-
allocation_obj.resources.add(Resource.objects.get(name="Tandon"))
283-
allocation_obj.save()
284-
285-
allocation_attribute_type_obj = AllocationAttributeType.objects.get(
286-
name="slurm_account_name"
287-
)
288-
AllocationAttribute.objects.get_or_create(
289-
allocation_attribute_type=allocation_attribute_type_obj,
290-
allocation=allocation_obj,
291-
value=f"torch_pr_{allocation_obj.project.pk}_Tandon",
292-
)
293-
294-
allocation_attribute_type_obj = AllocationAttributeType.objects.get(
295-
name="slurm_user_specs"
296-
)
297-
AllocationAttribute.objects.get_or_create(
298-
allocation_attribute_type=allocation_attribute_type_obj,
299-
allocation=allocation_obj,
300-
value="Fairshare=parent",
301-
)
302-
303-
allocation_user_obj = AllocationUser.objects.create(
304-
allocation=allocation_obj,
305-
user=pi1,
306-
status=AllocationUserStatusChoice.objects.get(name="Active"),
307-
)
308-
# Add university cluster
272+
# Add university allocation
309273
allocation_obj, _ = Allocation.objects.get_or_create(
310274
project=project_obj,
311275
status=AllocationStatusChoice.objects.get(name="Active"),
@@ -363,41 +327,13 @@ def handle(self, *args, **options):
363327
value="2022-01-01",
364328
)
365329

366-
allocation_user_obj = AllocationUser.objects.create(
367-
allocation=allocation_obj,
368-
user=pi1,
369-
status=AllocationUserStatusChoice.objects.get(name="Active"),
370-
)
371-
# Add project storage
372-
allocation_obj, _ = Allocation.objects.get_or_create(
373-
project=project_obj,
374-
status=AllocationStatusChoice.objects.get(name="Active"),
375-
start_date=start_date,
376-
end_date=end_date,
377-
quantity=10,
378-
is_changeable=True,
379-
justification="I need extra storage.",
380-
)
381-
382-
allocation_obj.resources.add(Resource.objects.get(name="Tandon"))
383-
allocation_obj.save()
384-
385-
allocation_attribute_type_obj = AllocationAttributeType.objects.get(
386-
name="slurm_account_name"
387-
)
388-
AllocationAttribute.objects.get_or_create(
389-
allocation_attribute_type=allocation_attribute_type_obj,
390-
allocation=allocation_obj,
391-
value=f"torch_pr_{allocation_obj.project.pk}_Tandon",
392-
)
393-
394330
allocation_user_obj = AllocationUser.objects.create(
395331
allocation=allocation_obj,
396332
user=pi1,
397333
status=AllocationUserStatusChoice.objects.get(name="Active"),
398334
)
399335

400-
# Add metered allocation
336+
# Add Tandon allocation
401337
allocation_obj, _ = Allocation.objects.get_or_create(
402338
project=project_obj,
403339
status=AllocationStatusChoice.objects.get(name="Active"),

0 commit comments

Comments
 (0)