Skip to content

Commit 7501776

Browse files
authored
Merge branch 'main' into feature-hide-field-of-science
2 parents 9afa0a6 + c39d8a1 commit 7501776

File tree

15 files changed

+304
-17
lines changed

15 files changed

+304
-17
lines changed

coldfront/config/core.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
"GRANT_ENABLE",
6969
"PUBLICATION_ENABLE",
7070
"FIELD_OF_SCIENCE_HIDE",
71+
"INVOICE_ENABLED",
72+
"PROJECT_ENABLE_PROJECT_REVIEW",
7173
]
7274

7375
ADMIN_COMMENTS_SHOW_EMPTY = ENV.bool("ADMIN_COMMENTS_SHOW_EMPTY", default=True)

coldfront/core/allocation/forms.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,24 @@ def clean(self):
226226
allocation_attribute.clean()
227227

228228

229+
class AllocationAttributeEditForm(forms.Form):
230+
attribute_pk = forms.IntegerField(required=False, disabled=True)
231+
name = forms.CharField(max_length=150, required=False, disabled=True)
232+
orig_value = forms.CharField(max_length=150, required=False, disabled=True)
233+
value = forms.CharField(max_length=150, required=False, disabled=False)
234+
235+
def __init__(self, *args, **kwargs):
236+
super().__init__(*args, **kwargs)
237+
self.fields["attribute_pk"].widget = forms.HiddenInput()
238+
239+
def clean(self):
240+
cleaned_data = super().clean()
241+
allocation_attribute = AllocationAttribute.objects.get(pk=cleaned_data.get("attribute_pk"))
242+
243+
allocation_attribute.value = cleaned_data.get("value")
244+
allocation_attribute.clean()
245+
246+
229247
class AllocationChangeForm(forms.Form):
230248
EXTENSION_CHOICES = [(0, "No Extension")]
231249
for choice in ALLOCATION_CHANGE_REQUEST_EXTENSION_DAYS:

coldfront/core/allocation/models.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
from django.contrib.auth.models import User
1111
from django.core.exceptions import ValidationError
1212
from django.db import models
13-
from django.utils.html import mark_safe
13+
from django.utils.html import escape, format_html
1414
from django.utils.module_loading import import_string
15+
from django.utils.safestring import SafeString
1516
from model_utils.models import TimeStampedModel
1617
from simple_history.models import HistoricalRecords
1718

@@ -147,16 +148,17 @@ def expires_in(self):
147148
return (self.end_date - datetime.date.today()).days
148149

149150
@property
150-
def get_information(self):
151+
def get_information(self) -> SafeString:
151152
"""
152153
Returns:
153-
str: the allocation's attribute type, usage out of total value, and usage out of total value as a percentage
154+
SafeString: the allocation's attribute type, usage out of total value, and usage out of total value as a percentage
154155
"""
155156

156-
html_string = ""
157+
html_string = escape("")
157158
for attribute in self.allocationattribute_set.all():
158159
if attribute.allocation_attribute_type.name in ALLOCATION_ATTRIBUTE_VIEW_LIST:
159-
html_string += "%s: %s <br>" % (attribute.allocation_attribute_type.name, attribute.value)
160+
html_substring = format_html("{}: {} <br>", attribute.allocation_attribute_type.name, attribute.value)
161+
html_string += html_substring
160162

161163
if hasattr(attribute, "allocationattributeusage"):
162164
try:
@@ -175,15 +177,16 @@ def get_information(self):
175177
"Allocation attribute '%s' == 0 but has a usage", attribute.allocation_attribute_type.name
176178
)
177179

178-
string = "{}: {}/{} ({} %) <br>".format(
180+
html_substring = format_html(
181+
"{}: {}/{} ({} %) <br>",
179182
attribute.allocation_attribute_type.name,
180183
attribute.allocationattributeusage.value,
181184
attribute.value,
182185
percent,
183186
)
184-
html_string += string
187+
html_string += html_substring
185188

186-
return mark_safe(html_string)
189+
return html_string
187190

188191
@property
189192
def get_resources_as_string(self):

coldfront/core/allocation/signals.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@
2121

2222
allocation_change_created = django.dispatch.Signal()
2323
# providing_args=["allocation_pk", "allocation_change_pk"]
24+
25+
allocation_attribute_changed = django.dispatch.Signal()
26+
# providing_args=["attribute_pk", "allocation_pk"]

coldfront/core/allocation/templates/allocation/allocation_allocationattribute_delete.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ <h2>Delete allocation attributes from allocation for project: {{allocation.proje
5454
<a class="btn btn-secondary mb-3" href="{% url 'allocation-detail' allocation.pk %}" role="button"><i
5555
class="fas fa-long-arrow-left" aria-hidden="true"></i> Back to Allocation</a>
5656
<div class="alert alert-info">
57-
No users to remove!
57+
No attributes to remove!
5858
</div>
5959
{% endif %}
6060

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{% extends "common/base.html" %} {% load crispy_forms_tags %} {% load static %}
2+
{% block title %} Allocation Change Detail {% endblock %} {% block content %}
3+
4+
<h2>
5+
Edit attributes for {{ allocation.get_parent_resource }} for project: {{ allocation.project.title }}
6+
</h2>
7+
<hr />
8+
<form method="post">
9+
<div class="card mb-3">
10+
<div class="card-header">
11+
<h3 class="d-inline">
12+
<i class="fas fa-info-circle" aria-hidden="true"></i> Allocation
13+
Attributes
14+
</h3>
15+
</div>
16+
<div class="card-body">
17+
{% if attributes %} {% csrf_token %}
18+
<div class="table-responsive">
19+
<table class="table table-bordered table-sm">
20+
<thead>
21+
<tr class="d-flex">
22+
<th class="col-6" scope="col">Attribute</th>
23+
<th class="col-6" scope="col">Set New Value</th>
24+
</tr>
25+
</thead>
26+
<tbody>
27+
{% for form in formset %}
28+
<tr class="d-flex">
29+
<td class="col-6">{{form.name.value}}</td>
30+
<td class="col-6">
31+
{{form.value}}
32+
<span
33+
class="d-none"
34+
id="change-indicator-{{form.attribute_pk.value}}"
35+
>
36+
<i class="fas fa-info-circle" aria-hidden="true"></i>
37+
Value changed
38+
</span>
39+
</td>
40+
</tr>
41+
{% endfor %}
42+
</tbody>
43+
</table>
44+
</div>
45+
{% else %}
46+
<div class="alert alert-info" role="alert">
47+
<i class="fas fa-info-circle" aria-hidden="true"></i>
48+
This allocation has no attributes.
49+
</div>
50+
{% endif %}
51+
52+
{% if attributes %}
53+
<input class="btn btn-success" type="submit" value="Confirm" />
54+
{% endif %}
55+
<a
56+
class="btn btn-secondary"
57+
href="{% url 'allocation-detail' allocation.pk %}"
58+
role="button"
59+
>Cancel</a
60+
>
61+
{{ formset.management_form }}
62+
</div>
63+
</div>
64+
</form>
65+
66+
<script>
67+
$(document).ready(function() {
68+
{% for form in formset %}
69+
$("#{{form.value.auto_id}}").on("input", function(){
70+
let change_indicator = $("#change-indicator-{{form.attribute_pk.value}}");
71+
if ($(this).val() != "{{form.orig_value.value|escapejs}}") {
72+
change_indicator.removeClass("d-none");
73+
} else {
74+
change_indicator.addClass("d-none");
75+
}
76+
});
77+
$("#{{form.value.auto_id}}").trigger("input");
78+
{% endfor %}
79+
})
80+
</script>
81+
{% endblock %}

coldfront/core/allocation/templates/allocation/allocation_detail.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ <h3 class="d-inline"><i class="fas fa-info-circle" aria-hidden="true"></i> EULA
214214
<h3 class="d-inline"><i class="fas fa-info-circle" aria-hidden="true"></i> Allocation Attributes</h3>
215215
<div class="float-right">
216216
{% if request.user.is_superuser %}
217+
{% if attributes %}
218+
<a class="btn btn-info" href="{% url 'allocation-attribute-edit' allocation.pk %}" role="button">
219+
<i class="fas fa-edit" aria-hidden="true"></i> Edit Allocation Attributes
220+
</a>
221+
{% endif %}
217222
<a class="btn btn-success" href="{% url 'allocation-attribute-add' allocation.pk %}" role="button">
218223
<i class="fas fa-plus" aria-hidden="true"></i> Add Allocation Attribute
219224
</a>
@@ -299,7 +304,6 @@ <h3 class="d-inline"><i class="fas fa-info-circle" aria-hidden="true"></i> Alloc
299304
{% else %}
300305
<td class="text-info">{{ change_request.status.name }}</td>
301306
{% endif %}
302-
</td>
303307
{% if change_request.notes %}
304308
<td>{{change_request.notes}}</td>
305309
{% else %}

coldfront/core/allocation/tests/test_views.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# SPDX-License-Identifier: AGPL-3.0-or-later
44

55
import logging
6+
from http import HTTPStatus
67

78
from django.test import TestCase
89
from django.urls import reverse
@@ -193,6 +194,51 @@ def test_allocationchangeview_post_no_change(self):
193194
self.assertEqual(len(AllocationChangeRequest.objects.all()), 0)
194195

195196

197+
class AllocationAttributeEditViewTest(AllocationViewBaseTest):
198+
"""Tests for AllocationAttributeEditView"""
199+
200+
def setUp(self):
201+
self.client.force_login(self.admin_user, backend=BACKEND)
202+
self.url = f"/allocation/{self.allocation.pk}/allocationattribute/edit"
203+
self.post_data = {
204+
"attributeform-0-value": self.allocation.get_attribute("Storage Quota (TB)"),
205+
"attributeform-INITIAL_FORMS": "1",
206+
"attributeform-MAX_NUM_FORMS": "1",
207+
"attributeform-MIN_NUM_FORMS": "0",
208+
"attributeform-TOTAL_FORMS": "1",
209+
}
210+
211+
def test_allocationattributeeditview_access(self):
212+
"""Test get request"""
213+
self.allocation_access_tstbase(self.url)
214+
utils.test_user_cannot_access(self, self.pi_user, self.url)
215+
utils.test_user_cannot_access(self, self.allocation_user, self.url)
216+
217+
def test_allocationattributeeditview_post_change_attr(self):
218+
"""Test post request to change attribute"""
219+
quota_orig = 100
220+
quota_new = 200
221+
222+
self.assertEqual(self.allocation.get_attribute("Storage Quota (TB)"), quota_orig)
223+
224+
self.post_data["attributeform-0-value"] = quota_new
225+
response = self.client.post(self.url, data=self.post_data, follow=True)
226+
self.assertEqual(response.status_code, HTTPStatus.OK)
227+
228+
self.assertEqual(self.allocation.get_attribute("Storage Quota (TB)"), quota_new)
229+
230+
def test_allocationattributeeditview_post_no_change(self):
231+
"""Test post request with no change"""
232+
quota_orig = 100
233+
234+
self.assertEqual(self.allocation.get_attribute("Storage Quota (TB)"), quota_orig)
235+
236+
response = self.client.post(self.url, data=self.post_data, follow=True)
237+
self.assertEqual(response.status_code, HTTPStatus.OK)
238+
239+
self.assertEqual(self.allocation.get_attribute("Storage Quota (TB)"), quota_orig)
240+
241+
196242
class AllocationDetailViewTest(AllocationViewBaseTest):
197243
"""Tests for AllocationDetailView"""
198244

@@ -214,12 +260,15 @@ def test_allocationdetail_requestchange_button(self):
214260
def test_allocationattribute_button_visibility(self):
215261
"""Test visibility of "Add Attribute" button for different user types"""
216262
# admin
263+
utils.page_contains_for_user(self, self.admin_user, self.url, "Edit Allocation Attribute")
217264
utils.page_contains_for_user(self, self.admin_user, self.url, "Add Allocation Attribute")
218265
utils.page_contains_for_user(self, self.admin_user, self.url, "Delete Allocation Attribute")
219266
# pi
267+
utils.page_does_not_contain_for_user(self, self.pi_user, self.url, "Edit Allocation Attribute")
220268
utils.page_does_not_contain_for_user(self, self.pi_user, self.url, "Add Allocation Attribute")
221269
utils.page_does_not_contain_for_user(self, self.pi_user, self.url, "Delete Allocation Attribute")
222270
# allocation user
271+
utils.page_does_not_contain_for_user(self, self.allocation_user, self.url, "Edit Allocation Attribute")
223272
utils.page_does_not_contain_for_user(self, self.allocation_user, self.url, "Add Allocation Attribute")
224273
utils.page_does_not_contain_for_user(self, self.allocation_user, self.url, "Delete Allocation Attribute")
225274

coldfront/core/allocation/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
name="allocation-attribute-add",
3333
),
3434
path("<int:pk>/change-request", allocation_views.AllocationChangeView.as_view(), name="allocation-change"),
35+
path(
36+
"<int:pk>/allocationattribute/edit",
37+
allocation_views.AllocationAttributeEditView.as_view(),
38+
name="allocation-attribute-edit",
39+
),
3540
path(
3641
"<int:pk>/allocationattribute/delete",
3742
allocation_views.AllocationAttributeDeleteView.as_view(),

0 commit comments

Comments
 (0)