Merge branch 'rrt/staging' into rrt/production
This commit is contained in:
commit
0a2ba844d6
|
@ -44,5 +44,11 @@ PB_STUDY_DETAILS_URL = environ.get('PB_STUDY_DETAILS_URL', default=PB_BASE_URL +
|
||||||
LDAP_URL = environ.get('LDAP_URL', default="ldap.virginia.edu").strip('/') # No trailing slash or http://
|
LDAP_URL = environ.get('LDAP_URL', default="ldap.virginia.edu").strip('/') # No trailing slash or http://
|
||||||
LDAP_TIMEOUT_SEC = int(environ.get('LDAP_TIMEOUT_SEC', default=1))
|
LDAP_TIMEOUT_SEC = int(environ.get('LDAP_TIMEOUT_SEC', default=1))
|
||||||
|
|
||||||
# Fallback emails
|
# Email configuration
|
||||||
FALLBACK_EMAILS = ['askresearch@virginia.edu', 'sartographysupport@googlegroups.com']
|
FALLBACK_EMAILS = ['askresearch@virginia.edu', 'sartographysupport@googlegroups.com']
|
||||||
|
MAIL_SERVER = environ.get('MAIL_SERVER', default='smtp.mailtrap.io')
|
||||||
|
MAIL_PORT = environ.get('MAIL_PORT', default=2525)
|
||||||
|
MAIL_USE_SSL = environ.get('MAIL_USE_SSL', default=False)
|
||||||
|
MAIL_USE_TLS = environ.get('MAIL_USE_TLS', default=True)
|
||||||
|
MAIL_USERNAME = environ.get('MAIL_USERNAME', default='5f012d0108d374')
|
||||||
|
MAIL_PASSWORD = environ.get('MAIL_PASSWORD', default='08442c04e98d50')
|
||||||
|
|
|
@ -54,12 +54,6 @@ if app.config['ENABLE_SENTRY']:
|
||||||
template_dir = os.getcwd() + '/crc/static/templates/mails'
|
template_dir = os.getcwd() + '/crc/static/templates/mails'
|
||||||
env = Environment(loader=FileSystemLoader(template_dir))
|
env = Environment(loader=FileSystemLoader(template_dir))
|
||||||
# Mail settings
|
# Mail settings
|
||||||
app.config['MAIL_SERVER']='smtp.mailtrap.io'
|
|
||||||
app.config['MAIL_PORT'] = 2525
|
|
||||||
app.config['MAIL_USERNAME'] = '5f012d0108d374'
|
|
||||||
app.config['MAIL_PASSWORD'] = '08442c04e98d50'
|
|
||||||
app.config['MAIL_USE_TLS'] = True
|
|
||||||
app.config['MAIL_USE_SSL'] = False
|
|
||||||
mail = Mail(app)
|
mail = Mail(app)
|
||||||
|
|
||||||
print('=== USING THESE CONFIG SETTINGS: ===')
|
print('=== USING THESE CONFIG SETTINGS: ===')
|
||||||
|
|
46
crc/api.yml
46
crc/api.yml
|
@ -806,12 +806,34 @@ paths:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Script"
|
$ref: "#/components/schemas/Script"
|
||||||
|
/approval-counts:
|
||||||
|
parameters:
|
||||||
|
- name: as_user
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: If provided, returns the approval counts for that user.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
get:
|
||||||
|
operationId: crc.api.approval.get_approval_counts
|
||||||
|
summary: Provides counts for approvals by status for the given user, or all users if no user is provided
|
||||||
|
tags:
|
||||||
|
- Approvals
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: An dictionary of Approval Statuses and the counts for each
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/ApprovalCounts"
|
||||||
/approval:
|
/approval:
|
||||||
parameters:
|
parameters:
|
||||||
- name: status
|
- name: status
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
description: If set to true, returns all the approvals with any status.
|
description: If provided, returns just approvals for the given status.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
- name: as_user
|
- name: as_user
|
||||||
|
@ -1286,4 +1308,26 @@ components:
|
||||||
type: number
|
type: number
|
||||||
format: integer
|
format: integer
|
||||||
example: 5
|
example: 5
|
||||||
|
ApprovalCounts:
|
||||||
|
properties:
|
||||||
|
PENDING:
|
||||||
|
type: number
|
||||||
|
format: integer
|
||||||
|
example: 5
|
||||||
|
APPROVED:
|
||||||
|
type: number
|
||||||
|
format: integer
|
||||||
|
example: 5
|
||||||
|
DECLINED:
|
||||||
|
type: number
|
||||||
|
format: integer
|
||||||
|
example: 5
|
||||||
|
CANCELED:
|
||||||
|
type: number
|
||||||
|
format: integer
|
||||||
|
example: 5
|
||||||
|
AWAITING:
|
||||||
|
type: number
|
||||||
|
format: integer
|
||||||
|
example: 5
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,56 @@ from crc.services.approval_service import ApprovalService
|
||||||
from crc.services.ldap_service import LdapService
|
from crc.services.ldap_service import LdapService
|
||||||
|
|
||||||
|
|
||||||
|
# Returns counts of approvals in each status group assigned to the given user.
|
||||||
|
# The goal is to return results as quickly as possible.
|
||||||
|
def get_approval_counts(as_user=None):
|
||||||
|
uid = as_user or g.user.uid
|
||||||
|
|
||||||
|
db_user_approvals = db.session.query(ApprovalModel)\
|
||||||
|
.filter_by(approver_uid=uid)\
|
||||||
|
.filter(ApprovalModel.status != ApprovalStatus.CANCELED.name)\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
study_ids = [a.study_id for a in db_user_approvals]
|
||||||
|
print('study_ids', study_ids)
|
||||||
|
|
||||||
|
db_other_approvals = db.session.query(ApprovalModel)\
|
||||||
|
.filter(ApprovalModel.study_id.in_(study_ids))\
|
||||||
|
.filter(ApprovalModel.approver_uid != uid)\
|
||||||
|
.filter(ApprovalModel.status != ApprovalStatus.CANCELED.name)\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
# Make a dict of the other approvals where the key is the study id and the value is the approval
|
||||||
|
# TODO: This won't work if there are more than 2 approvals with the same study_id
|
||||||
|
other_approvals = {}
|
||||||
|
for approval in db_other_approvals:
|
||||||
|
other_approvals[approval.study_id] = approval
|
||||||
|
|
||||||
|
counts = {}
|
||||||
|
for status in ApprovalStatus:
|
||||||
|
counts[status.name] = 0
|
||||||
|
|
||||||
|
for approval in db_user_approvals:
|
||||||
|
# Check if another approval has the same study id
|
||||||
|
if approval.study_id in other_approvals:
|
||||||
|
other_approval = other_approvals[approval.study_id]
|
||||||
|
|
||||||
|
# Other approval takes precedence over this one
|
||||||
|
if other_approval.id < approval.id:
|
||||||
|
if other_approval.status == ApprovalStatus.PENDING.name:
|
||||||
|
counts[ApprovalStatus.AWAITING.name] += 1
|
||||||
|
elif other_approval.status == ApprovalStatus.DECLINED.name:
|
||||||
|
counts[ApprovalStatus.DECLINED.name] += 1
|
||||||
|
elif other_approval.status == ApprovalStatus.CANCELED.name:
|
||||||
|
counts[ApprovalStatus.CANCELED.name] += 1
|
||||||
|
elif other_approval.status == ApprovalStatus.APPROVED.name:
|
||||||
|
counts[approval.status] += 1
|
||||||
|
else:
|
||||||
|
counts[approval.status] += 1
|
||||||
|
|
||||||
|
return counts
|
||||||
|
|
||||||
|
|
||||||
def get_approvals(status=None, as_user=None):
|
def get_approvals(status=None, as_user=None):
|
||||||
#status = ApprovalStatus.PENDING.value
|
#status = ApprovalStatus.PENDING.value
|
||||||
user = g.user.uid
|
user = g.user.uid
|
||||||
|
@ -31,7 +81,7 @@ def get_approvals_for_study(study_id=None):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
# ----- Being decent into madness ---- #
|
# ----- Begin descent into madness ---- #
|
||||||
def get_csv():
|
def get_csv():
|
||||||
"""A damn lie, it's a json file. A huge bit of a one-off for RRT, but 3 weeks of midnight work can convince a
|
"""A damn lie, it's a json file. A huge bit of a one-off for RRT, but 3 weeks of midnight work can convince a
|
||||||
man to do just about anything"""
|
man to do just about anything"""
|
||||||
|
@ -81,12 +131,14 @@ def get_csv():
|
||||||
errors.append("Error pulling data for workflow #%i: %s" % (approval.workflow_id, str(e)))
|
errors.append("Error pulling data for workflow #%i: %s" % (approval.workflow_id, str(e)))
|
||||||
return {"results": output, "errors": errors }
|
return {"results": output, "errors": errors }
|
||||||
|
|
||||||
|
|
||||||
def extract_value(task, key):
|
def extract_value(task, key):
|
||||||
if key in task['data']:
|
if key in task['data']:
|
||||||
return pickle.loads(b64decode(task['data'][key]['__bytes__']))
|
return pickle.loads(b64decode(task['data'][key]['__bytes__']))
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def find_task(uuid, task):
|
def find_task(uuid, task):
|
||||||
if task['id']['__uuid__'] == uuid:
|
if task['id']['__uuid__'] == uuid:
|
||||||
return task
|
return task
|
||||||
|
@ -96,6 +148,7 @@ def find_task(uuid, task):
|
||||||
return task
|
return task
|
||||||
# ----- come back to the world of the living ---- #
|
# ----- come back to the world of the living ---- #
|
||||||
|
|
||||||
|
|
||||||
def update_approval(approval_id, body):
|
def update_approval(approval_id, body):
|
||||||
if approval_id is None:
|
if approval_id is None:
|
||||||
raise ApiError('unknown_approval', 'Please provide a valid Approval ID.')
|
raise ApiError('unknown_approval', 'Please provide a valid Approval ID.')
|
||||||
|
@ -113,5 +166,9 @@ def update_approval(approval_id, body):
|
||||||
session.add(approval_model)
|
session.add(approval_model)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
# Called only to send emails
|
||||||
|
approver = body['approver']['uid']
|
||||||
|
ApprovalService.update_approval(approval_id, approver)
|
||||||
|
|
||||||
result = ApprovalSchema().dump(approval_model)
|
result = ApprovalSchema().dump(approval_model)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -111,13 +111,14 @@ class ApprovalService(object):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_approval(approval_id, approver_uid, status):
|
def update_approval(approval_id, approver_uid):
|
||||||
"""Update a specific approval"""
|
"""Update a specific approval"""
|
||||||
db_approval = session.query(ApprovalModel).get(approval_id)
|
db_approval = session.query(ApprovalModel).get(approval_id)
|
||||||
|
status = db_approval.status
|
||||||
if db_approval:
|
if db_approval:
|
||||||
db_approval.status = status
|
# db_approval.status = status
|
||||||
session.add(db_approval)
|
# session.add(db_approval)
|
||||||
session.commit()
|
# session.commit()
|
||||||
if status == ApprovalStatus.APPROVED.value:
|
if status == ApprovalStatus.APPROVED.value:
|
||||||
# second_approval = ApprovalModel().query.filter_by(
|
# second_approval = ApprovalModel().query.filter_by(
|
||||||
# study_id=db_approval.study_id, workflow_id=db_approval.workflow_id,
|
# study_id=db_approval.study_id, workflow_id=db_approval.workflow_id,
|
||||||
|
@ -125,7 +126,7 @@ class ApprovalService(object):
|
||||||
# if second_approval:
|
# if second_approval:
|
||||||
# send rrp approval request for second approver
|
# send rrp approval request for second approver
|
||||||
ldap_service = LdapService()
|
ldap_service = LdapService()
|
||||||
pi_user_info = ldap_service.user_info(model.study.primary_investigator_id)
|
pi_user_info = ldap_service.user_info(db_approval.study.primary_investigator_id)
|
||||||
approver_info = ldap_service.user_info(approver_uid)
|
approver_info = ldap_service.user_info(approver_uid)
|
||||||
# send rrp submission
|
# send rrp submission
|
||||||
send_ramp_up_approved_email(
|
send_ramp_up_approved_email(
|
||||||
|
@ -135,7 +136,7 @@ class ApprovalService(object):
|
||||||
)
|
)
|
||||||
elif status == ApprovalStatus.DECLINED.value:
|
elif status == ApprovalStatus.DECLINED.value:
|
||||||
ldap_service = LdapService()
|
ldap_service = LdapService()
|
||||||
pi_user_info = ldap_service.user_info(model.study.primary_investigator_id)
|
pi_user_info = ldap_service.user_info(db_approval.study.primary_investigator_id)
|
||||||
approver_info = ldap_service.user_info(approver_uid)
|
approver_info = ldap_service.user_info(approver_uid)
|
||||||
# send rrp submission
|
# send rrp submission
|
||||||
send_ramp_up_denied_email(
|
send_ramp_up_denied_email(
|
||||||
|
|
|
@ -11,7 +11,6 @@ def send_ramp_up_submission_email(sender, recipients, approver_1, approver_2=Non
|
||||||
msg = Message('Research Ramp-up Plan Submitted',
|
msg = Message('Research Ramp-up Plan Submitted',
|
||||||
sender=sender,
|
sender=sender,
|
||||||
recipients=recipients)
|
recipients=recipients)
|
||||||
|
|
||||||
from crc import env, mail
|
from crc import env, mail
|
||||||
template = env.get_template('ramp_up_submission.txt')
|
template = env.get_template('ramp_up_submission.txt')
|
||||||
template_vars = {'approver_1': approver_1, 'approver_2': approver_2}
|
template_vars = {'approver_1': approver_1, 'approver_2': approver_2}
|
||||||
|
@ -28,7 +27,6 @@ def send_ramp_up_approval_request_email(sender, recipients, primary_investigator
|
||||||
msg = Message('Research Ramp-up Plan Approval Request',
|
msg = Message('Research Ramp-up Plan Approval Request',
|
||||||
sender=sender,
|
sender=sender,
|
||||||
recipients=recipients)
|
recipients=recipients)
|
||||||
|
|
||||||
from crc import env, mail
|
from crc import env, mail
|
||||||
template = env.get_template('ramp_up_approval_request.txt')
|
template = env.get_template('ramp_up_approval_request.txt')
|
||||||
template_vars = {'primary_investigator': primary_investigator}
|
template_vars = {'primary_investigator': primary_investigator}
|
||||||
|
@ -45,7 +43,6 @@ def send_ramp_up_approval_request_first_review_email(sender, recipients, primary
|
||||||
msg = Message('Research Ramp-up Plan Approval Request',
|
msg = Message('Research Ramp-up Plan Approval Request',
|
||||||
sender=sender,
|
sender=sender,
|
||||||
recipients=recipients)
|
recipients=recipients)
|
||||||
|
|
||||||
from crc import env, mail
|
from crc import env, mail
|
||||||
template = env.get_template('ramp_up_approval_request_first_review.txt')
|
template = env.get_template('ramp_up_approval_request_first_review.txt')
|
||||||
template_vars = {'primary_investigator': primary_investigator}
|
template_vars = {'primary_investigator': primary_investigator}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<p>Your Research Ramp-up Plan has been denied by {{ approver_1 }}. Please return to the Research Ramp-up Plan application and review the comments from {{ approver_1 }} on the home page. Next, open the application and locate the first step where changes are needed. Continue to complete additional steps saving your work along the way. Review your revised Research Ramp-up Plan and res-submit for approval.</p>
|
<p>Your Research Ramp-up Plan has been denied by {{ approver }}. Please return to the Research Ramp-up Plan application and review the comments from {{ approver }} on the home page. Next, open the application and locate the first step where changes are needed. Continue to complete additional steps saving your work along the way. Review your revised Research Ramp-up Plan and res-submit for approval.</p>
|
|
@ -105,7 +105,7 @@ class TestApprovals(BaseTest):
|
||||||
def test_accept_approval(self):
|
def test_accept_approval(self):
|
||||||
approval = session.query(ApprovalModel).filter_by(approver_uid='dhf8r').first()
|
approval = session.query(ApprovalModel).filter_by(approver_uid='dhf8r').first()
|
||||||
data = {'id': approval.id,
|
data = {'id': approval.id,
|
||||||
"approver_uid": "dhf8r",
|
"approver": {"uid": "dhf8r"},
|
||||||
'message': "Approved. I like the cut of your jib.",
|
'message': "Approved. I like the cut of your jib.",
|
||||||
'status': ApprovalStatus.APPROVED.value}
|
'status': ApprovalStatus.APPROVED.value}
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ class TestApprovals(BaseTest):
|
||||||
def test_decline_approval(self):
|
def test_decline_approval(self):
|
||||||
approval = session.query(ApprovalModel).filter_by(approver_uid='dhf8r').first()
|
approval = session.query(ApprovalModel).filter_by(approver_uid='dhf8r').first()
|
||||||
data = {'id': approval.id,
|
data = {'id': approval.id,
|
||||||
"approver_uid": "dhf8r",
|
"approver": {"uid": "dhf8r"},
|
||||||
'message': "Approved. I find the cut of your jib lacking.",
|
'message': "Approved. I find the cut of your jib lacking.",
|
||||||
'status': ApprovalStatus.DECLINED.value}
|
'status': ApprovalStatus.DECLINED.value}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue