Skip to content
Open
27 changes: 26 additions & 1 deletion coldfront/core/project/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
from django.shortcuts import get_object_or_404
from django.db.models import Q

import coldfront.core.project.forms_.new_project_forms.request_forms as request_forms
from coldfront.core.project.models import (Project, ProjectReview,
ProjectUserRoleChoice)
from coldfront.core.user.models import UserProfile
from django.contrib.auth.models import User
from coldfront.core.user.utils_.host_user_utils import eligible_host_project_users
from coldfront.core.utils.common import import_from_settings
from coldfront.core.resource.utils import get_compute_resource_names
Expand Down Expand Up @@ -209,8 +212,15 @@ class ReviewDenyForm(forms.Form):
widget=forms.Textarea(attrs={'rows': 3}))


class ReviewStatusForm(forms.Form):
class ReviewStatusForm(request_forms.SavioProjectNewPIForm,
request_forms.SavioProjectExistingPIForm):

# PI = SavioProjectExistingPIForm.PI
# first_name = forms.CharField(max_length=30, required=True)
# middle_name = forms.CharField(max_length=30, required=False)
# last_name = forms.CharField(max_length=150, required=True)
# email = forms.EmailField(max_length=100, required=True)

status = forms.ChoiceField(
choices=(
('', 'Select one.'),
Expand All @@ -231,6 +241,21 @@ class ReviewStatusForm(forms.Form):
required=False,
widget=forms.Textarea(attrs={'rows': 3}))

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['PI'].label_from_instance = self.label_from_instance
self.fields['PI'].empty_label = 'New PI'
self.fields['PI'].help_text = (
'Please confirm the PI for this project. If the PI is not listed, '
'select "New PI" and provide the PI\'s information below.')
self.fields['first_name'].required = False
self.fields['last_name'].required = False
self.fields['email'].required = False

@staticmethod
def label_from_instance(obj):
return f'{obj.first_name} {obj.last_name} ({obj.email})'

def clean(self):
cleaned_data = super().clean()
status = cleaned_data.get('status', 'Pending')
Expand Down
22 changes: 20 additions & 2 deletions coldfront/core/project/forms_/new_project_forms/request_forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from coldfront.core.allocation.forms import AllocationPeriodChoiceField
from coldfront.core.allocation.models import AllocationPeriod
from coldfront.core.project.forms import DisabledChoicesSelectWidget
from coldfront.core.project.models import Project
from coldfront.core.project.utils_.new_project_utils import non_denied_new_project_request_statuses
from coldfront.core.project.utils_.new_project_utils import pis_with_new_project_requests_pks
Expand Down Expand Up @@ -147,6 +146,24 @@ def label_from_instance(self, obj):
return f'{obj.first_name} {obj.last_name} ({obj.email})'


class DisabledChoicesSelectWidget(forms.Select):

def __init__(self, *args, **kwargs):
self.disabled_choices = kwargs.pop('disabled_choices', set())
super().__init__(*args, **kwargs)

def create_option(self, name, value, label, selected, index, subindex=None,
attrs=None):
option = super().create_option(
name, value, label, selected, index, subindex=subindex,
attrs=attrs)
try:
if int(str(value)) in self.disabled_choices:
option['attrs']['disabled'] = True
except Exception:
pass
return option

class SavioProjectExistingPIForm(forms.Form):

PI = PIChoiceField(
Expand Down Expand Up @@ -234,7 +251,8 @@ class SavioProjectNewPIForm(forms.Form):
def clean_email(self):
cleaned_data = super().clean()
email = cleaned_data['email'].lower()
if (User.objects.filter(username=email).exists() or
# "email and" for project.forms.ReviewStatusForm
if email and (User.objects.filter(username=email).exists() or
User.objects.filter(email=email).exists()):
raise forms.ValidationError(
'A user with that email address already exists.')
Expand Down
33 changes: 30 additions & 3 deletions coldfront/core/project/views_/new_project_views/approval_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from coldfront.core.project.utils_.new_project_utils import send_project_request_pooling_email
from coldfront.core.project.utils_.new_project_utils import VectorProjectProcessingRunner
from coldfront.core.project.utils_.new_project_utils import vector_request_state_status
from django.contrib.auth.models import User
from coldfront.core.resource.utils_.allowance_utils.computing_allowance import ComputingAllowance
from coldfront.core.resource.utils_.allowance_utils.interface import ComputingAllowanceInterface
from coldfront.core.utils.common import display_time_zone_current_date
Expand All @@ -32,7 +33,7 @@
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import UserPassesTestMixin
from django.db import transaction
from django.db import IntegrityError, transaction
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
Expand Down Expand Up @@ -591,6 +592,8 @@ class SavioProjectReviewEligibilityView(LoginRequiredMixin,
template_name = (
'project/project_request/savio/project_review_eligibility.html')

logger = logging.getLogger(__name__)

def test_func(self):
"""UserPassesTestMixin tests."""
if self.request.user.is_superuser:
Expand All @@ -606,7 +609,7 @@ def dispatch(self, request, *args, **kwargs):
if redirect is not None:
return redirect
return super().dispatch(request, *args, **kwargs)

def form_valid(self, form):
form_data = form.cleaned_data
status = form_data['status']
Expand All @@ -622,9 +625,32 @@ def form_valid(self, form):
if status == 'Denied':
runner = ProjectDenialRunner(self.request_obj)
runner.run()

if form_data['PI'] != self.request_obj.pi:
if form_data['PI'] is not None:
self.request_obj.pi = form_data['PI']
self.request_obj.save()
elif all([form_data['first_name'],
form_data['last_name'],
form_data['email']]):
try:
self.request_obj.pi = User.objects.create(
username=form_data['email'],
first_name=form_data['first_name'],
last_name=form_data['last_name'],
email=form_data['email'],
is_active=True)
self.request_obj.pi.save()
self.request_obj.save()
except IntegrityError as e:
self.logger.error(f'User {form_data["email"]} unexpectedly exists.')
raise e
else:
message = 'PI information is incomplete.'
messages.error(self.request, message)
return self.form_invalid(form)

self.request_obj.save()

message = (
f'Eligibility status for request {self.request_obj.pk} has been '
f'set to {status}.')
Expand All @@ -642,6 +668,7 @@ def get_context_data(self, **kwargs):
def get_initial(self):
initial = super().get_initial()
eligibility = self.request_obj.state['eligibility']
initial['PI'] = self.request_obj.pi
initial['status'] = eligibility['status']
initial['justification'] = eligibility['justification']
return initial
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def edit_extra_fields_url(pk):
def test_new_project(self):
"""Test that the MOU notification task, MOU upload, and MOU download
features work as expected."""
eligibility = { 'status': 'Approved' }
eligibility = { 'PI': self.request.pi.pk, 'status': 'Approved' }
readiness = { 'status': 'Approved' }
extra_fields = {
'course_name': 'TEST 101',
Expand Down