Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combined email expiry notifications with more information #413

Merged
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
052c5eb
Tested Django querying and reverse related object lookup for revamp i…
aestoltm Jun 8, 2022
2eb6ca5
Planned structure for revamp implementation
aestoltm Jun 9, 2022
0246602
First attempt at email revamp
aestoltm Jun 9, 2022
fb113bd
Email revamp close to completion, need more rigorous testing.
aestoltm Jun 13, 2022
2e82345
Commit before to allow changes from master to be pulled and merged
aestoltm Jun 14, 2022
36f46fe
Merge remote-tracking branch 'origin/master' into #270-combine-alloca…
aestoltm Jun 14, 2022
11f8b81
Updated attach allocation links to projects and when they are expiring
aestoltm Jun 14, 2022
922b29b
Structure of email revamp is complete. Just need to add resource name…
aestoltm Jun 15, 2022
971065f
Added resource parent name to each allocation that is expiring or exp…
aestoltm Jun 16, 2022
b6a051b
Final commit before creating PR, need to merge CCR master branch into…
aestoltm Jun 20, 2022
96fe44f
Merge remote-tracking branch 'origin/master' into #270-combine-alloca…
aestoltm Jun 20, 2022
a5ecc3f
First attempt at adding email notifications on expired allocations fo…
aestoltm Jun 28, 2022
2a64403
Updated admin emails
aestoltm Jun 28, 2022
17ebbe7
Changes to allocation attribute types, global config for admin emails…
aestoltm Aug 5, 2022
fa465b5
Removed print statements from env file debugging
aestoltm Aug 5, 2022
789df6b
Updates to admin email template and logic, reverted allocation attrib…
aestoltm Aug 5, 2022
ed0f2b8
Adjust email receiver for admin emails
aestoltm Aug 9, 2022
63f0806
Removed unused imports
aestoltm Aug 9, 2022
db5fbb2
Changes to expiring email subject and first line
aestoltm Aug 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions coldfront/config/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
EMAIL_OPT_OUT_INSTRUCTION_URL = ENV.str('EMAIL_OPT_OUT_INSTRUCTION_URL', default='')
EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS = ENV.list('EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS', cast=int, default=[7, 14, 30])
EMAIL_SIGNATURE = ENV.str('EMAIL_SIGNATURE', default='', multiline=True)
EMAIL_ADMINS_ON_ALLOCATION_EXPIRE = ENV.bool('EMAIL_ADMINS_ON_ALLOCATION_EXPIRE', default=False)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Command(BaseCommand):

def handle(self, *args, **options):

for attribute_type in ('Date', 'Float', 'Int', 'Text', 'Yes/No',
for attribute_type in ('Date', 'Float', 'Int', 'Text', 'Yes/No', 'No',
'Attribute Expanded Text'):
AttributeType.objects.get_or_create(name=attribute_type)

Expand Down
283 changes: 152 additions & 131 deletions coldfront/core/allocation/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
# import the logging library
import logging

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured

from coldfront.core.allocation.models import (Allocation, AllocationAttribute,
from coldfront.core.allocation.models import (Allocation,
AllocationStatusChoice)
from coldfront.core.utils.common import get_domain_url, import_from_settings
from coldfront.core.user.models import User
from coldfront.core.utils.common import import_from_settings
from coldfront.core.utils.mail import send_email_template

# Get an instance of a logger
Expand All @@ -25,6 +23,8 @@
EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS = import_from_settings(
'EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS', [7, ])

EMAIL_ADMINS_ON_ALLOCATION_EXPIRE = import_from_settings('EMAIL_ADMINS_ON_ALLOCATION_EXPIRE')
EMAIL_ADMIN_LIST = import_from_settings('EMAIL_ADMIN_LIST')

def update_statuses():

Expand All @@ -41,148 +41,169 @@ def update_statuses():


def send_expiry_emails():
# Allocations expiring

for days_remaining in sorted(set(EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS)):
expring_in_days = datetime.datetime.today(
) + datetime.timedelta(days=days_remaining)

for allocation_obj in Allocation.objects.filter(status__name='Active', end_date=expring_in_days):

expire_notification = allocation_obj.allocationattribute_set.filter(
allocation_attribute_type__name='EXPIRE NOTIFICATION').first()
if expire_notification and expire_notification.value == 'No':
continue

cloud_usage_notification = allocation_obj.allocationattribute_set.filter(
allocation_attribute_type__name='CLOUD_USAGE_NOTIFICATION').first()
if cloud_usage_notification and cloud_usage_notification.value == 'No':
continue


allocation_renew_url = '{}/{}/{}/{}'.format(
CENTER_BASE_URL.strip('/'), 'allocation', allocation_obj.pk, 'renew')

resource_name = allocation_obj.get_parent_resource.name

template_context = {
'center_name': CENTER_NAME,
'allocation_type': resource_name,
'expring_in_days': days_remaining,
'allocation_renew_url': allocation_renew_url,
'project_renewal_help_url': CENTER_PROJECT_RENEWAL_HELP_URL,
'opt_out_instruction_url': EMAIL_OPT_OUT_INSTRUCTION_URL,
'signature': EMAIL_SIGNATURE

}

email_receiver_list = []
for allocation_user in allocation_obj.project.projectuser_set.all():
if (allocation_user.enable_notifications and
allocation_obj.allocationuser_set.filter(
user=allocation_user.user, status__name='Active')
and allocation_user.user.email not in email_receiver_list):

email_receiver_list.append(allocation_user.user.email)

send_email_template('Allocation to {} expiring in {} days'.format(resource_name, days_remaining),
'email/allocation_expiring.txt',
template_context,
EMAIL_SENDER,
email_receiver_list
)

logger.info('Allocation to {} expiring in {} days email sent to PI {}.'.format(
resource_name, days_remaining, allocation_obj.project.pi.username))

# Allocations expiring today
today = datetime.datetime.now().strftime('%Y-%m-%d')
#Allocations expiring soon
for user in User.objects.all():
projectdict = {}
expirationdict = {}
email_receiver_list = []
for days_remaining in sorted(set(EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS)):

expring_in_days = (datetime.datetime.today(
) + datetime.timedelta(days=days_remaining)).date()

for allocationuser in user.allocationuser_set.all():
allocation = allocationuser.allocation

if (((allocation.status.name in ['Active', 'Payment Pending', 'Payment Requested', 'Unpaid']) and (allocation.end_date == expring_in_days))):

project_url = f'{CENTER_BASE_URL.strip("/")}/{"project"}/{allocation.project.pk}/'

if (allocation.status.name in ['Payment Pending', 'Payment Requested', 'Unpaid']):
allocation_renew_url = f'{CENTER_BASE_URL.strip("/")}/{"allocation"}/{allocation.pk}/'
else:
allocation_renew_url = f'{CENTER_BASE_URL.strip("/")}/{"allocation"}/{allocation.pk}/{"renew"}/'

resource_name = allocation.get_parent_resource.name

template_context = {
'center_name': CENTER_NAME,
'expring_in_days': days_remaining,
'project_dict': projectdict,
'expiration_dict': expirationdict,
'expiration_days': sorted(set(EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS)),
'project_renewal_help_url': CENTER_PROJECT_RENEWAL_HELP_URL,
'opt_out_instruction_url': EMAIL_OPT_OUT_INSTRUCTION_URL,
'signature': EMAIL_SIGNATURE
}

expire_notification = allocation.allocationattribute_set.filter(
allocation_attribute_type__name='EXPIRE NOTIFICATION').first()
if expire_notification and expire_notification.value == 'No':
continue

cloud_usage_notification = allocation.allocationattribute_set.filter(
allocation_attribute_type__name='CLOUD_USAGE_NOTIFICATION').first()
if cloud_usage_notification and cloud_usage_notification.value == 'No':
continue

for projectuser in allocation.project.projectuser_set.filter(user=user, status__name='Active'):
if ((projectuser.enable_notifications) and
(allocationuser.user == user and allocationuser.status.name == 'Active')):

if (user.email not in email_receiver_list):
email_receiver_list.append(user.email)

if days_remaining not in expirationdict:
expirationdict[days_remaining] = []
expirationdict[days_remaining].append((project_url, allocation_renew_url, resource_name))
else:
expirationdict[days_remaining].append((project_url, allocation_renew_url, resource_name))

if allocation.project.title not in projectdict:
projectdict[allocation.project.title] = (project_url, allocation.project.pi.username,)

if email_receiver_list:

send_email_template('Your access to resource(s) are expiring soon',
'email/allocation_expiring.txt',
template_context,
EMAIL_SENDER,
email_receiver_list
)

logger.debug(f'Allocation(s) expiring in soon, email sent to user {user}.')

#Allocations expired
admin_projectdict = {}
admin_allocationdict = {}
for user in User.objects.all():
projectdict = {}
allocationdict = {}
email_receiver_list = []

expring_in_days = (datetime.datetime.today() + datetime.timedelta(days=-1)).date()

for allocationuser in user.allocationuser_set.all():
allocation = allocationuser.allocation

for allocation_attribute in AllocationAttribute.objects.filter(
value=today,
allocation_attribute_type__name='send_expiry_email_on_date'):
if (allocation.end_date == expring_in_days):
project_url = f'{CENTER_BASE_URL.strip("/")}/{"project"}/{allocation.project.pk}/'

allocation_obj = allocation_attribute.allocation
days_remaining = allocation_obj.expires_in
allocation_renew_url = f'{CENTER_BASE_URL.strip("/")}/{"allocation"}/{allocation.pk}/{"renew"}/'

allocation_renew_url = '{}/{}/{}/{}'.format(
CENTER_BASE_URL.strip('/'), 'allocation', allocation_obj.pk, 'renew')
allocation_url = f'{CENTER_BASE_URL.strip("/")}/{"allocation"}/{allocation.pk}/'

resource_name = allocation_obj.get_parent_resource.name
resource_name = allocation.get_parent_resource.name

template_context = {
'center_name': CENTER_NAME,
'allocation_type': resource_name,
'expring_in_days': days_remaining,
'allocation_renew_url': allocation_renew_url,
'project_renewal_help_url': CENTER_PROJECT_RENEWAL_HELP_URL,
'opt_out_instruction_url': EMAIL_OPT_OUT_INSTRUCTION_URL,
'signature': EMAIL_SIGNATURE
template_context = {
'center_name': CENTER_NAME,
'project_dict': projectdict,
'allocation_dict': allocationdict,
'project_renewal_help_url': CENTER_PROJECT_RENEWAL_HELP_URL,
'opt_out_instruction_url': EMAIL_OPT_OUT_INSTRUCTION_URL,
'signature': EMAIL_SIGNATURE
}

}
expire_notification = allocation.allocationattribute_set.filter(
allocation_attribute_type__name='EXPIRE NOTIFICATION').first()

email_receiver_list = []
for allocation_user in allocation_obj.project.projectuser_set.all():
if (allocation_user.enable_notifications and
allocation_obj.allocationuser_set.filter(
user=allocation_user.user, status__name='Active')
and allocation_user.user.email not in email_receiver_list):
for projectuser in allocation.project.projectuser_set.filter(user=user, status__name='Active'):
if ((projectuser.enable_notifications) and
(allocationuser.user == user and allocationuser.status.name == 'Active')):

email_receiver_list.append(allocation_user.user.email)
if expire_notification and expire_notification.value == 'Yes':

send_email_template('Allocation to {} expiring in {} days'.format(resource_name, days_remaining),
'email/allocation_expiring.txt',
template_context,
EMAIL_SENDER,
email_receiver_list
)
if (user.email not in email_receiver_list):
email_receiver_list.append(user.email)

logger.info('Allocation to {} expiring in {} days email sent to PI {}.'.format(
resource_name, days_remaining, allocation_obj.project.pi.username))
if project_url not in allocationdict:
allocationdict[project_url] = []
allocationdict[project_url].append({allocation_renew_url : resource_name})
else:
if {allocation_renew_url : resource_name} not in allocationdict[project_url]:
allocationdict[project_url].append({allocation_renew_url : resource_name})

# Expired allocations
if allocation.project.title not in projectdict:
projectdict[allocation.project.title] = (project_url, allocation.project.pi.username)

expring_in_days = datetime.datetime.today() + datetime.timedelta(days=-1)
if EMAIL_ADMINS_ON_ALLOCATION_EXPIRE:

if project_url not in admin_allocationdict:
admin_allocationdict[project_url] = []
admin_allocationdict[project_url].append({allocation_url : resource_name})
else:
if {allocation_url : resource_name} not in admin_allocationdict[project_url]:
admin_allocationdict[project_url].append({allocation_url : resource_name})

for allocation_obj in Allocation.objects.filter(end_date=expring_in_days):
if allocation.project.title not in admin_projectdict:
admin_projectdict[allocation.project.title] = (project_url, allocation.project.pi.username)

expire_notification = allocation_obj.allocationattribute_set.filter(
allocation_attribute_type__name='EXPIRE NOTIFICATION').first()
if expire_notification and expire_notification.value == 'No':
continue

if email_receiver_list:

resource_name = allocation_obj.get_parent_resource.name
send_email_template('Your access to resource(s) have expired',
'email/allocation_expired.txt',
template_context,
EMAIL_SENDER,
email_receiver_list
)

allocation_renew_url = '{}/{}/{}/{}'.format(
CENTER_BASE_URL.strip('/'), 'allocation', allocation_obj.pk, 'renew')
logger.debug(f'Allocation(s) expired email sent to user {user}.')

project_url = '{}/{}/{}/'.format(CENTER_BASE_URL.strip('/'),
'project', allocation_obj.project.pk)
if EMAIL_ADMINS_ON_ALLOCATION_EXPIRE:

template_context = {
'center_name': CENTER_NAME,
'allocation_type': resource_name,
'project_renewal_help_url': CENTER_PROJECT_RENEWAL_HELP_URL,
'project_url': project_url,
'opt_out_instruction_url': EMAIL_OPT_OUT_INSTRUCTION_URL,
'signature': EMAIL_SIGNATURE
}
if admin_projectdict:

email_receiver_list = []
for allocation_user in allocation_obj.project.projectuser_set.all():
if (allocation_user.enable_notifications and
allocation_obj.allocationuser_set.filter(
user=allocation_user.user, status__name='Active')
and allocation_user.user.email not in email_receiver_list):

email_receiver_list.append(allocation_user.user.email)

send_email_template('Allocation to {} has expired'.format(resource_name),
'email/allocation_expired.txt',
template_context,
EMAIL_SENDER,
email_receiver_list
)

logger.info('Allocation to {} expired email sent to PI {}.'.format(
resource_name, allocation_obj.project.pi.username))
admin_template_context = {
'project_dict': admin_projectdict,
'allocation_dict': admin_allocationdict,
'signature': EMAIL_SIGNATURE
}

send_email_template('Allocation(s) have expired',
'email/admin_allocation_expired.txt',
admin_template_context,
EMAIL_SENDER,
[EMAIL_ADMIN_LIST,]
)
12 changes: 12 additions & 0 deletions coldfront/templates/email/admin_allocation_expired.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Dear System Administrator,

Allocations to your system resources have expired. You should confirm access to these resources has been removed for all users on the allocations.
Below is a list of the allocations that expired today:

Expired Allocation(s):{% for project_key, project_url in project_dict.items %}
{% spaceless %} {% for allocation_key, allocation_value in allocation_dict.items %}{% if allocation_key == project_url.0 %}{% for allocation in allocation_value %}{% for allocation_url, resource_name in allocation.items %}
{{ resource_name }} : {{ allocation_url }}{% endfor %}{% endfor %}{% endif %}{% endfor %}{% endspaceless %}
{% endfor %}

Thank you,
{{ signature }}
13 changes: 10 additions & 3 deletions coldfront/templates/email/allocation_expired.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
Dear {{center_name}} user,

Your allocation for access to {{ allocation_type }} has expired. Any accounts under this allocation are now unable to
access this resource. If you wish to continue using it, the group owner responsible for this project must create a new
allocation request. {{project_url}}
Your access to {{center_name}} resources has expired. Any accounts under these allocation(s) are now unable to
access the associated resource. If you wish to continue using them, the group owners responsible for the following project(s) must create a new
allocation request(s). Below is a list of links to your project(s) containing at least one expired allocation:
{% for project_key, project_url in project_dict.items %}
Project Title: {{project_key}}
Project URL: {{ project_url.0 }}
Project PI: {{ project_url.1 }}

{% spaceless %} {% for allocation_key, allocation_value in allocation_dict.items %}{% if allocation_key == project_url.0 %}Expired Allocation(s):{% for allocation in allocation_value %}{% for allocation_url, resource_name in allocation.items %}
{{ resource_name }}{% endfor %}{% endfor %}{% endif %}{% endfor %}{% endspaceless %}
{% endfor %}
The group owner responsible for this project is required to renew it on a yearly basis. If you have not done so in the
last year, you will be required to before requesting a new allocation.
For more information about the yearly renewal, visit our knowledge base: {{ project_renewal_help_url }}.
Expand Down
Loading