Merge branch 'rrt/testing' into rrt/staging
This commit is contained in:
commit
ebb37b4555
1
Pipfile
1
Pipfile
|
@ -39,6 +39,7 @@ ldap3 = "*"
|
|||
gunicorn = "*"
|
||||
werkzeug = "*"
|
||||
sentry-sdk = {extras = ["flask"],version = "==0.14.4"}
|
||||
flask-mail = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "54d9d51360f54762138a3acc7696badd1d711e7b1dde9e2d82aa706e40c17102"
|
||||
"sha256": "6c89585086260ebcb41918b8ef3b1d9e189e1b492208d3ff000a138bc2f2fcee"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -104,10 +104,10 @@
|
|||
},
|
||||
"celery": {
|
||||
"hashes": [
|
||||
"sha256:5147662e23dc6bc39c17a2cbc9a148debe08ecfb128b0eded14a0d9c81fc5742",
|
||||
"sha256:df2937b7536a2a9b18024776a3a46fd281721813636c03a5177fa02fe66078f6"
|
||||
"sha256:9ae2e73b93cc7d6b48b56aaf49a68c91752d0ffd7dfdcc47f842ca79a6f13eae",
|
||||
"sha256:c2037b6a8463da43b19969a0fc13f9023ceca6352b4dd51be01c66fbbb13647e"
|
||||
],
|
||||
"version": "==4.4.3"
|
||||
"version": "==4.4.4"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
|
@ -276,6 +276,13 @@
|
|||
"index": "pypi",
|
||||
"version": "==3.0.8"
|
||||
},
|
||||
"flask-mail": {
|
||||
"hashes": [
|
||||
"sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.9.1"
|
||||
},
|
||||
"flask-marshmallow": {
|
||||
"hashes": [
|
||||
"sha256:6e6aec171b8e092e0eafaf035ff5b8637bf3a58ab46f568c4c1bab02f2a3c196",
|
||||
|
@ -344,11 +351,11 @@
|
|||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
|
||||
"sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
|
||||
"sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545",
|
||||
"sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"
|
||||
],
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==1.6.0"
|
||||
"version": "==1.6.1"
|
||||
},
|
||||
"inflection": {
|
||||
"hashes": [
|
||||
|
@ -387,10 +394,10 @@
|
|||
},
|
||||
"kombu": {
|
||||
"hashes": [
|
||||
"sha256:ab0afaa5388dd2979cbc439d3623b86a4f7a58d41f621096bef7767c37bc2505",
|
||||
"sha256:aece08f48706743aaa1b9d607fee300559481eafcc5ee56451aa0ef867a3be07"
|
||||
"sha256:437b9cdea193cc2ed0b8044c85fd0f126bb3615ca2f4d4a35b39de7cacfa3c1a",
|
||||
"sha256:dc282bb277197d723bccda1a9ba30a27a28c9672d0ab93e9e51bb05a37bd29c3"
|
||||
],
|
||||
"version": "==4.6.9"
|
||||
"version": "==4.6.10"
|
||||
},
|
||||
"ldap3": {
|
||||
"hashes": [
|
||||
|
@ -479,11 +486,11 @@
|
|||
},
|
||||
"marshmallow": {
|
||||
"hashes": [
|
||||
"sha256:c2673233aa21dde264b84349dc2fd1dce5f30ed724a0a00e75426734de5b84ab",
|
||||
"sha256:f88fe96434b1f0f476d54224d59333eba8ca1a203a2695683c1855675c4049a7"
|
||||
"sha256:35ee2fb188f0bd9fc1cf9ac35e45fd394bd1c153cee430745a465ea435514bd5",
|
||||
"sha256:9aa20f9b71c992b4782dad07c51d92884fd0f7c5cb9d3c737bea17ec1bad765f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.6.0"
|
||||
"version": "==3.6.1"
|
||||
},
|
||||
"marshmallow-enum": {
|
||||
"hashes": [
|
||||
|
@ -503,29 +510,29 @@
|
|||
},
|
||||
"numpy": {
|
||||
"hashes": [
|
||||
"sha256:00d7b54c025601e28f468953d065b9b121ddca7fff30bed7be082d3656dd798d",
|
||||
"sha256:02ec9582808c4e48be4e93cd629c855e644882faf704bc2bd6bbf58c08a2a897",
|
||||
"sha256:0e6f72f7bb08f2f350ed4408bb7acdc0daba637e73bce9f5ea2b207039f3af88",
|
||||
"sha256:1be2e96314a66f5f1ce7764274327fd4fb9da58584eaff00b5a5221edefee7d6",
|
||||
"sha256:2466fbcf23711ebc5daa61d28ced319a6159b260a18839993d871096d66b93f7",
|
||||
"sha256:2b573fcf6f9863ce746e4ad00ac18a948978bb3781cffa4305134d31801f3e26",
|
||||
"sha256:3f0dae97e1126f529ebb66f3c63514a0f72a177b90d56e4bce8a0b5def34627a",
|
||||
"sha256:50fb72bcbc2cf11e066579cb53c4ca8ac0227abb512b6cbc1faa02d1595a2a5d",
|
||||
"sha256:57aea170fb23b1fd54fa537359d90d383d9bf5937ee54ae8045a723caa5e0961",
|
||||
"sha256:709c2999b6bd36cdaf85cf888d8512da7433529f14a3689d6e37ab5242e7add5",
|
||||
"sha256:7d59f21e43bbfd9a10953a7e26b35b6849d888fc5a331fa84a2d9c37bd9fe2a2",
|
||||
"sha256:904b513ab8fbcbdb062bed1ce2f794ab20208a1b01ce9bd90776c6c7e7257032",
|
||||
"sha256:96dd36f5cdde152fd6977d1bbc0f0561bccffecfde63cd397c8e6033eb66baba",
|
||||
"sha256:9933b81fecbe935e6a7dc89cbd2b99fea1bf362f2790daf9422a7bb1dc3c3085",
|
||||
"sha256:bbcc85aaf4cd84ba057decaead058f43191cc0e30d6bc5d44fe336dc3d3f4509",
|
||||
"sha256:dccd380d8e025c867ddcb2f84b439722cf1f23f3a319381eac45fd077dee7170",
|
||||
"sha256:e22cd0f72fc931d6abc69dc7764484ee20c6a60b0d0fee9ce0426029b1c1bdae",
|
||||
"sha256:ed722aefb0ebffd10b32e67f48e8ac4c5c4cf5d3a785024fdf0e9eb17529cd9d",
|
||||
"sha256:efb7ac5572c9a57159cf92c508aad9f856f1cb8e8302d7fdb99061dbe52d712c",
|
||||
"sha256:efdba339fffb0e80fcc19524e4fdbda2e2b5772ea46720c44eaac28096d60720",
|
||||
"sha256:f22273dd6a403ed870207b853a856ff6327d5cbce7a835dfa0645b3fc00273ec"
|
||||
"sha256:0172304e7d8d40e9e49553901903dc5f5a49a703363ed756796f5808a06fc233",
|
||||
"sha256:34e96e9dae65c4839bd80012023aadd6ee2ccb73ce7fdf3074c62f301e63120b",
|
||||
"sha256:3676abe3d621fc467c4c1469ee11e395c82b2d6b5463a9454e37fe9da07cd0d7",
|
||||
"sha256:3dd6823d3e04b5f223e3e265b4a1eae15f104f4366edd409e5a5e413a98f911f",
|
||||
"sha256:4064f53d4cce69e9ac613256dc2162e56f20a4e2d2086b1956dd2fcf77b7fac5",
|
||||
"sha256:4674f7d27a6c1c52a4d1aa5f0881f1eff840d2206989bae6acb1c7668c02ebfb",
|
||||
"sha256:7d42ab8cedd175b5ebcb39b5208b25ba104842489ed59fbb29356f671ac93583",
|
||||
"sha256:965df25449305092b23d5145b9bdaeb0149b6e41a77a7d728b1644b3c99277c1",
|
||||
"sha256:9c9d6531bc1886454f44aa8f809268bc481295cf9740827254f53c30104f074a",
|
||||
"sha256:a78e438db8ec26d5d9d0e584b27ef25c7afa5a182d1bf4d05e313d2d6d515271",
|
||||
"sha256:a7acefddf994af1aeba05bbbafe4ba983a187079f125146dc5859e6d817df824",
|
||||
"sha256:a87f59508c2b7ceb8631c20630118cc546f1f815e034193dc72390db038a5cb3",
|
||||
"sha256:ac792b385d81151bae2a5a8adb2b88261ceb4976dbfaaad9ce3a200e036753dc",
|
||||
"sha256:b03b2c0badeb606d1232e5f78852c102c0a7989d3a534b3129e7856a52f3d161",
|
||||
"sha256:b39321f1a74d1f9183bf1638a745b4fd6fe80efbb1f6b32b932a588b4bc7695f",
|
||||
"sha256:cae14a01a159b1ed91a324722d746523ec757357260c6804d11d6147a9e53e3f",
|
||||
"sha256:cd49930af1d1e49a812d987c2620ee63965b619257bd76eaaa95870ca08837cf",
|
||||
"sha256:e15b382603c58f24265c9c931c9a45eebf44fe2e6b4eaedbb0d025ab3255228b",
|
||||
"sha256:e91d31b34fc7c2c8f756b4e902f901f856ae53a93399368d9a0dc7be17ed2ca0",
|
||||
"sha256:ef627986941b5edd1ed74ba89ca43196ed197f1a206a3f18cc9faf2fb84fd675",
|
||||
"sha256:f718a7949d1c4f622ff548c572e0c03440b49b9531ff00e4ed5738b459f011e8"
|
||||
],
|
||||
"version": "==1.18.4"
|
||||
"version": "==1.18.5"
|
||||
},
|
||||
"openapi-spec-validator": {
|
||||
"hashes": [
|
||||
|
@ -917,11 +924,11 @@
|
|||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
|
||||
"sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
|
||||
"sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545",
|
||||
"sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"
|
||||
],
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==1.6.0"
|
||||
"version": "==1.6.1"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
|
@ -968,11 +975,11 @@
|
|||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:95c710d0a72d91c13fae35dce195633c929c3792f54125919847fdcdf7caa0d3",
|
||||
"sha256:eb2b5e935f6a019317e455b6da83dd8650ac9ffd2ee73a7b657a30873d67a698"
|
||||
"sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1",
|
||||
"sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.4.2"
|
||||
"version": "==5.4.3"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
|
@ -983,10 +990,10 @@
|
|||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:3de2e41158cb650b91f9654cbf9a3e053cee0719c9df4ddc11e4b568669e9829",
|
||||
"sha256:b651b6b081476420e4e9ae61239ac4c1b49d0c5ace42b2e81dc2ff49ed50c566"
|
||||
"sha256:980fbf4f3c196c0f329cdcd1e84c554d6a211f18e252e525a0cf4223154a41d6",
|
||||
"sha256:edbc2b718b4db6cdf393eefe3a420183947d6aa312505ce6754516f458ff8830"
|
||||
],
|
||||
"version": "==0.2.2"
|
||||
"version": "==0.2.3"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
|
|
|
@ -42,6 +42,13 @@ PB_REQUIRED_DOCS_URL = environ.get('PB_REQUIRED_DOCS_URL', default=PB_BASE_URL +
|
|||
PB_STUDY_DETAILS_URL = environ.get('PB_STUDY_DETAILS_URL', default=PB_BASE_URL + "study?studyid=%i")
|
||||
|
||||
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=3))
|
||||
|
||||
LDAP_TIMEOUT_SEC = int(environ.get('LDAP_TIMEOUT_SEC', default=1))
|
||||
|
||||
# Email configuration
|
||||
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')
|
||||
|
|
|
@ -3,8 +3,10 @@ import os
|
|||
import sentry_sdk
|
||||
|
||||
import connexion
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from flask_cors import CORS
|
||||
from flask_marshmallow import Marshmallow
|
||||
from flask_mail import Mail
|
||||
from flask_migrate import Migrate
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||
|
@ -48,6 +50,12 @@ if app.config['ENABLE_SENTRY']:
|
|||
integrations=[FlaskIntegration()]
|
||||
)
|
||||
|
||||
# Jinja environment definition, used to render mail templates
|
||||
template_dir = os.getcwd() + '/crc/static/templates/mails'
|
||||
env = Environment(loader=FileSystemLoader(template_dir))
|
||||
# Mail settings
|
||||
mail = Mail(app)
|
||||
|
||||
print('=== USING THESE CONFIG SETTINGS: ===')
|
||||
print('DB_HOST = ', )
|
||||
print('CORS_ALLOW_ORIGINS = ', app.config['CORS_ALLOW_ORIGINS'])
|
||||
|
@ -73,3 +81,9 @@ def load_example_rrt_data():
|
|||
from example_data import ExampleDataLoader
|
||||
ExampleDataLoader.clean_db()
|
||||
ExampleDataLoader().load_rrt()
|
||||
|
||||
@app.cli.command()
|
||||
def clear_db():
|
||||
"""Load example data into the database."""
|
||||
from example_data import ExampleDataLoader
|
||||
ExampleDataLoader.clean_db()
|
||||
|
|
58
crc/api.yml
58
crc/api.yml
|
@ -806,14 +806,42 @@ paths:
|
|||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Script"
|
||||
/approval:
|
||||
/approval-counts:
|
||||
parameters:
|
||||
- name: everything
|
||||
- name: as_user
|
||||
in: query
|
||||
required: false
|
||||
description: If set to true, returns all the approvals known to the system.
|
||||
description: If provided, returns the approval counts for that user.
|
||||
schema:
|
||||
type: boolean
|
||||
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:
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
required: false
|
||||
description: If provided, returns just approvals for the given status.
|
||||
schema:
|
||||
type: string
|
||||
- name: as_user
|
||||
in: query
|
||||
required: false
|
||||
description: If provided, returns the approval results as they would appear for that user.
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
operationId: crc.api.approval.get_approvals
|
||||
summary: Provides a list of workflows approvals
|
||||
|
@ -1280,4 +1308,26 @@ components:
|
|||
type: number
|
||||
format: integer
|
||||
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,11 +13,63 @@ from crc.services.approval_service import ApprovalService
|
|||
from crc.services.ldap_service import LdapService
|
||||
|
||||
|
||||
def get_approvals(everything=False):
|
||||
if everything:
|
||||
approvals = ApprovalService.get_all_approvals(include_cancelled=True)
|
||||
else:
|
||||
approvals = ApprovalService.get_approvals_per_user(g.user.uid, include_cancelled=False)
|
||||
# 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):
|
||||
#status = ApprovalStatus.PENDING.value
|
||||
user = g.user.uid
|
||||
if as_user:
|
||||
user = as_user
|
||||
approvals = ApprovalService.get_approvals_per_user(user, status,
|
||||
include_cancelled=False)
|
||||
results = ApprovalSchema(many=True).dump(approvals)
|
||||
return results
|
||||
|
||||
|
@ -29,7 +81,7 @@ def get_approvals_for_study(study_id=None):
|
|||
return results
|
||||
|
||||
|
||||
# ----- Being decent into madness ---- #
|
||||
# ----- Begin descent into madness ---- #
|
||||
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
|
||||
man to do just about anything"""
|
||||
|
@ -79,12 +131,14 @@ def get_csv():
|
|||
errors.append("Error pulling data for workflow #%i: %s" % (approval.workflow_id, str(e)))
|
||||
return {"results": output, "errors": errors }
|
||||
|
||||
|
||||
def extract_value(task, key):
|
||||
if key in task['data']:
|
||||
return pickle.loads(b64decode(task['data'][key]['__bytes__']))
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def find_task(uuid, task):
|
||||
if task['id']['__uuid__'] == uuid:
|
||||
return task
|
||||
|
@ -94,6 +148,7 @@ def find_task(uuid, task):
|
|||
return task
|
||||
# ----- come back to the world of the living ---- #
|
||||
|
||||
|
||||
def update_approval(approval_id, body):
|
||||
if approval_id is None:
|
||||
raise ApiError('unknown_approval', 'Please provide a valid Approval ID.')
|
||||
|
@ -111,5 +166,9 @@ def update_approval(approval_id, body):
|
|||
session.add(approval_model)
|
||||
session.commit()
|
||||
|
||||
# Called only to send emails
|
||||
approver = body['approver']['uid']
|
||||
ApprovalService.update_approval(approval_id, approver)
|
||||
|
||||
result = ApprovalSchema().dump(approval_model)
|
||||
return result
|
||||
|
|
|
@ -20,6 +20,9 @@ class ApprovalStatus(enum.Enum):
|
|||
DECLINED = "DECLINED" # rejected by the reviewer
|
||||
CANCELED = "CANCELED" # The document was replaced with a new version and this review is no longer needed.
|
||||
|
||||
# Used for overall status only, never set on a task.
|
||||
AWAITING = "AWAITING" # awaiting another approval
|
||||
|
||||
|
||||
class ApprovalFile(db.Model):
|
||||
file_data_id = db.Column(db.Integer, db.ForeignKey(FileDataModel.id), primary_key=True)
|
||||
|
@ -81,13 +84,13 @@ class Approval(object):
|
|||
instance.associated_files = []
|
||||
for approval_file in model.approval_files:
|
||||
try:
|
||||
# fixme: This is slow because we are doing a ton of queries to find the irb code.
|
||||
extra_info = doc_dictionary[approval_file.file_data.file_model.irb_doc_code]
|
||||
except:
|
||||
extra_info = None
|
||||
associated_file = {}
|
||||
associated_file['id'] = approval_file.file_data.file_model.id
|
||||
if extra_info:
|
||||
irb_doc_code = approval_file.file_data.file_model.irb_doc_code
|
||||
associated_file['name'] = '_'.join((extra_info['category1'],
|
||||
approval_file.file_data.file_model.name))
|
||||
associated_file['description'] = extra_info['description']
|
||||
|
|
|
@ -11,7 +11,8 @@ class RequestApproval(Script):
|
|||
return """
|
||||
Creates an approval request on this workflow, by the given approver_uid(s),"
|
||||
Takes multiple arguments, which should point to data located in current task
|
||||
or be quoted strings.
|
||||
or be quoted strings. The order is important. Approvals will be processed
|
||||
in this order.
|
||||
|
||||
Example:
|
||||
RequestApproval approver1 "dhf8r"
|
||||
|
|
|
@ -2,7 +2,7 @@ from datetime import datetime
|
|||
|
||||
from sqlalchemy import desc
|
||||
|
||||
from crc import db, session
|
||||
from crc import app, db, session
|
||||
from crc.api.common import ApiError
|
||||
|
||||
from crc.models.approval import ApprovalModel, ApprovalStatus, ApprovalFile, Approval
|
||||
|
@ -10,45 +10,79 @@ from crc.models.study import StudyModel
|
|||
from crc.models.workflow import WorkflowModel
|
||||
from crc.services.file_service import FileService
|
||||
from crc.services.ldap_service import LdapService
|
||||
|
||||
from crc.services.mails import (
|
||||
send_ramp_up_submission_email,
|
||||
send_ramp_up_approval_request_email,
|
||||
send_ramp_up_approval_request_first_review_email,
|
||||
send_ramp_up_approved_email,
|
||||
send_ramp_up_denied_email,
|
||||
send_ramp_up_denied_email_to_approver
|
||||
)
|
||||
|
||||
class ApprovalService(object):
|
||||
"""Provides common tools for working with an Approval"""
|
||||
|
||||
@staticmethod
|
||||
def __one_approval_from_study(study, approver_uid = None, include_cancelled=True):
|
||||
def __one_approval_from_study(study, approver_uid = None, status=None,
|
||||
include_cancelled=True):
|
||||
"""Returns one approval, with all additional approvals as 'related_approvals',
|
||||
the main approval can be pinned to an approver with an optional argument.
|
||||
Will return null if no approvals exist on the study."""
|
||||
main_approval = None
|
||||
related_approvals = []
|
||||
query = db.session.query(ApprovalModel).\
|
||||
filter(ApprovalModel.study_id == study.id)
|
||||
query = db.session.query(ApprovalModel).filter(ApprovalModel.study_id == study.id)
|
||||
if not include_cancelled:
|
||||
query=query.filter(ApprovalModel.status != ApprovalStatus.CANCELED.value)
|
||||
approvals = query.all() # All non-cancelled approvals.
|
||||
|
||||
approvals = query.all()
|
||||
for approval_model in approvals:
|
||||
if approval_model.approver_uid == approver_uid:
|
||||
main_approval = Approval.from_model(approval_model)
|
||||
main_approval = approval_model
|
||||
else:
|
||||
related_approvals.append(Approval.from_model(approval_model))
|
||||
related_approvals.append(approval_model)
|
||||
|
||||
# IF WE ARE JUST RETURNING ALL OF THE APPROVALS PER STUDY
|
||||
if not main_approval and len(related_approvals) > 0:
|
||||
main_approval = related_approvals[0]
|
||||
related_approvals = related_approvals[1:]
|
||||
if len(related_approvals) > 0:
|
||||
main_approval.related_approvals = related_approvals
|
||||
|
||||
if main_approval is not None: # May be null if the study has no approvals.
|
||||
final_status = ApprovalService.__calculate_overall_approval_status(main_approval, related_approvals)
|
||||
if status and final_status != status: return # Now that we are certain of the status, filter on it.
|
||||
|
||||
main_approval = Approval.from_model(main_approval)
|
||||
main_approval.status = final_status
|
||||
for ra in related_approvals:
|
||||
main_approval.related_approvals.append(Approval.from_model(ra))
|
||||
|
||||
return main_approval
|
||||
|
||||
@staticmethod
|
||||
def get_approvals_per_user(approver_uid, include_cancelled=False):
|
||||
def __calculate_overall_approval_status(approval, related):
|
||||
# In the case of pending approvals, check to see if there is a related approval
|
||||
# that proceeds this approval - and if it is declined, or still pending, then change
|
||||
# the state of the approval to be Declined, or Waiting respectively.
|
||||
if approval.status == ApprovalStatus.PENDING.value:
|
||||
for ra in related:
|
||||
if ra.id < approval.id:
|
||||
if ra.status == ApprovalStatus.DECLINED.value or ra.status == ApprovalStatus.CANCELED.value:
|
||||
return ra.status # If any prior approval id declined or cancelled so is this approval.
|
||||
elif ra.status == ApprovalStatus.PENDING.value:
|
||||
return ApprovalStatus.AWAITING.value # if any prior approval is pending, then this is waiting.
|
||||
return approval.status
|
||||
else:
|
||||
return approval.status
|
||||
|
||||
@staticmethod
|
||||
def get_approvals_per_user(approver_uid, status=None, include_cancelled=False):
|
||||
"""Returns a list of approval objects (not db models) for the given
|
||||
approver. """
|
||||
studies = db.session.query(StudyModel).join(ApprovalModel).\
|
||||
filter(ApprovalModel.approver_uid == approver_uid).all()
|
||||
approvals = []
|
||||
for study in studies:
|
||||
approval = ApprovalService.__one_approval_from_study(study, approver_uid, include_cancelled)
|
||||
approval = ApprovalService.__one_approval_from_study(study, approver_uid,
|
||||
status, include_cancelled)
|
||||
if approval:
|
||||
approvals.append(approval)
|
||||
return approvals
|
||||
|
@ -77,13 +111,53 @@ class ApprovalService(object):
|
|||
|
||||
|
||||
@staticmethod
|
||||
def update_approval(approval_id, approver_uid, status):
|
||||
def update_approval(approval_id, approver_uid):
|
||||
"""Update a specific approval"""
|
||||
db_approval = session.query(ApprovalModel).get(approval_id)
|
||||
status = db_approval.status
|
||||
if db_approval:
|
||||
db_approval.status = status
|
||||
session.add(db_approval)
|
||||
session.commit()
|
||||
# db_approval.status = status
|
||||
# session.add(db_approval)
|
||||
# session.commit()
|
||||
if status == ApprovalStatus.APPROVED.value:
|
||||
# second_approval = ApprovalModel().query.filter_by(
|
||||
# study_id=db_approval.study_id, workflow_id=db_approval.workflow_id,
|
||||
# status=ApprovalStatus.PENDING.value, version=db_approval.version).first()
|
||||
# if second_approval:
|
||||
# send rrp approval request for second approver
|
||||
ldap_service = LdapService()
|
||||
pi_user_info = ldap_service.user_info(db_approval.study.primary_investigator_id)
|
||||
approver_info = ldap_service.user_info(approver_uid)
|
||||
# send rrp submission
|
||||
send_ramp_up_approved_email(
|
||||
'askresearch@virginia.edu',
|
||||
[pi_user_info.email_address],
|
||||
f'{approver_info.display_name} - ({approver_info.uid})'
|
||||
)
|
||||
elif status == ApprovalStatus.DECLINED.value:
|
||||
ldap_service = LdapService()
|
||||
pi_user_info = ldap_service.user_info(db_approval.study.primary_investigator_id)
|
||||
approver_info = ldap_service.user_info(approver_uid)
|
||||
# send rrp submission
|
||||
send_ramp_up_denied_email(
|
||||
'askresearch@virginia.edu',
|
||||
[pi_user_info.email_address],
|
||||
f'{approver_info.display_name} - ({approver_info.uid})'
|
||||
)
|
||||
first_approval = ApprovalModel().query.filter_by(
|
||||
study_id=db_approval.study_id, workflow_id=db_approval.workflow_id,
|
||||
status=ApprovalStatus.APPROVED.value, version=db_approval.version).first()
|
||||
if first_approval:
|
||||
# Second approver denies
|
||||
first_approver_info = ldap_service.user_info(first_approval.approver_uid)
|
||||
approver_email = [first_approver_info.email_address] if first_approver_info.email_address else app.config['FALLBACK_EMAILS']
|
||||
# send rrp denied by second approver email to first approver
|
||||
send_ramp_up_denied_email_to_approver(
|
||||
'askresearch@virginia.edu',
|
||||
approver_email,
|
||||
f'{pi_user_info.display_name} - ({pi_user_info.uid})',
|
||||
f'{approver_info.display_name} - ({approver_info.uid})'
|
||||
)
|
||||
# TODO: Log update action by approver_uid - maybe ?
|
||||
return db_approval
|
||||
|
||||
|
@ -133,10 +207,35 @@ class ApprovalService(object):
|
|||
message="", date_created=datetime.now(),
|
||||
version=version)
|
||||
approval_files = ApprovalService._create_approval_files(workflow_data_files, model)
|
||||
|
||||
# Check approvals count
|
||||
approvals_count = ApprovalModel().query.filter_by(study_id=study_id, workflow_id=workflow_id,
|
||||
version=version).count()
|
||||
|
||||
db.session.add(model)
|
||||
db.session.add_all(approval_files)
|
||||
db.session.commit()
|
||||
|
||||
# Send first email
|
||||
if approvals_count == 0:
|
||||
ldap_service = LdapService()
|
||||
pi_user_info = ldap_service.user_info(model.study.primary_investigator_id)
|
||||
approver_info = ldap_service.user_info(approver_uid)
|
||||
# send rrp submission
|
||||
send_ramp_up_submission_email(
|
||||
'askresearch@virginia.edu',
|
||||
[pi_user_info.email_address],
|
||||
f'{approver_info.display_name} - ({approver_info.uid})'
|
||||
)
|
||||
# send rrp approval request for first approver
|
||||
# enhance the second part in case it bombs
|
||||
approver_email = [approver_info.email_address] if approver_info.email_address else app.config['FALLBACK_EMAILS']
|
||||
send_ramp_up_approval_request_first_review_email(
|
||||
'askresearch@virginia.edu',
|
||||
approver_email,
|
||||
f'{pi_user_info.display_name} - ({pi_user_info.uid})'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _create_approval_files(workflow_data_files, approval):
|
||||
"""Currently based exclusively on the status of files associated with a workflow."""
|
||||
|
|
|
@ -18,7 +18,7 @@ class LdapService(object):
|
|||
user_or_last_name_search = "(&(objectclass=person)(|(uid=%s*)(sn=%s*)))"
|
||||
cn_single_search = '(&(objectclass=person)(cn=%s*))'
|
||||
cn_double_search = '(&(objectclass=person)(&(cn=%s*)(cn=*%s*)))'
|
||||
|
||||
temp_cache = {}
|
||||
conn = None
|
||||
|
||||
@staticmethod
|
||||
|
@ -43,6 +43,7 @@ class LdapService(object):
|
|||
def user_info(uva_uid):
|
||||
user_info = db.session.query(LdapModel).filter(LdapModel.uid == uva_uid).first()
|
||||
if not user_info:
|
||||
app.logger.info("No cache for " + uva_uid)
|
||||
search_string = LdapService.uid_search_string % uva_uid
|
||||
conn = LdapService.__get_conn()
|
||||
conn.search(LdapService.search_base, search_string, attributes=LdapService.attributes)
|
||||
|
@ -51,6 +52,7 @@ class LdapService(object):
|
|||
entry = conn.entries[0]
|
||||
user_info = LdapModel.from_entry(entry)
|
||||
db.session.add(user_info)
|
||||
db.session.commit()
|
||||
return user_info
|
||||
|
||||
@staticmethod
|
||||
|
@ -69,7 +71,7 @@ class LdapService(object):
|
|||
# Search by user_id or last name
|
||||
search_string = LdapService.user_or_last_name_search % (query, query)
|
||||
results = []
|
||||
print(search_string)
|
||||
app.logger.info(search_string)
|
||||
try:
|
||||
conn = LdapService.__get_conn()
|
||||
conn.search(LdapService.search_base, search_string, attributes=LdapService.attributes)
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
import os
|
||||
|
||||
from flask import render_template, render_template_string
|
||||
from flask_mail import Message
|
||||
|
||||
|
||||
# TODO: Extract common mailing code into its own function
|
||||
|
||||
def send_ramp_up_submission_email(sender, recipients, approver_1, approver_2=None):
|
||||
try:
|
||||
msg = Message('Research Ramp-up Plan Submitted',
|
||||
sender=sender,
|
||||
recipients=recipients)
|
||||
from crc import env, mail
|
||||
template = env.get_template('ramp_up_submission.txt')
|
||||
template_vars = {'approver_1': approver_1, 'approver_2': approver_2}
|
||||
msg.body = template.render(template_vars)
|
||||
template = env.get_template('ramp_up_submission.html')
|
||||
msg.html = template.render(template_vars)
|
||||
|
||||
mail.send(msg)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def send_ramp_up_approval_request_email(sender, recipients, primary_investigator):
|
||||
try:
|
||||
msg = Message('Research Ramp-up Plan Approval Request',
|
||||
sender=sender,
|
||||
recipients=recipients)
|
||||
from crc import env, mail
|
||||
template = env.get_template('ramp_up_approval_request.txt')
|
||||
template_vars = {'primary_investigator': primary_investigator}
|
||||
msg.body = template.render(template_vars)
|
||||
template = env.get_template('ramp_up_approval_request.html')
|
||||
msg.html = template.render(template_vars)
|
||||
|
||||
mail.send(msg)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def send_ramp_up_approval_request_first_review_email(sender, recipients, primary_investigator):
|
||||
try:
|
||||
msg = Message('Research Ramp-up Plan Approval Request',
|
||||
sender=sender,
|
||||
recipients=recipients)
|
||||
from crc import env, mail
|
||||
template = env.get_template('ramp_up_approval_request_first_review.txt')
|
||||
template_vars = {'primary_investigator': primary_investigator}
|
||||
msg.body = template.render(template_vars)
|
||||
template = env.get_template('ramp_up_approval_request_first_review.html')
|
||||
msg.html = template.render(template_vars)
|
||||
|
||||
mail.send(msg)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def send_ramp_up_approved_email(sender, recipients, approver_1, approver_2=None):
|
||||
try:
|
||||
msg = Message('Research Ramp-up Plan Approved',
|
||||
sender=sender,
|
||||
recipients=recipients)
|
||||
|
||||
from crc import env, mail
|
||||
template = env.get_template('ramp_up_approved.txt')
|
||||
template_vars = {'approver_1': approver_1, 'approver_2': approver_2}
|
||||
msg.body = template.render(template_vars)
|
||||
template = env.get_template('ramp_up_approved.html')
|
||||
msg.html = template.render(template_vars)
|
||||
|
||||
mail.send(msg)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def send_ramp_up_denied_email(sender, recipients, approver):
|
||||
try:
|
||||
msg = Message('Research Ramp-up Plan Denied',
|
||||
sender=sender,
|
||||
recipients=recipients)
|
||||
|
||||
from crc import env, mail
|
||||
template = env.get_template('ramp_up_denied.txt')
|
||||
template_vars = {'approver': approver}
|
||||
msg.body = template.render(template_vars)
|
||||
template = env.get_template('ramp_up_denied.html')
|
||||
msg.html = template.render(template_vars)
|
||||
|
||||
mail.send(msg)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def send_ramp_up_denied_email_to_approver(sender, recipients, primary_investigator, approver_2):
|
||||
try:
|
||||
msg = Message('Research Ramp-up Plan Denied',
|
||||
sender=sender,
|
||||
recipients=recipients)
|
||||
|
||||
from crc import env, mail
|
||||
template = env.get_template('ramp_up_denied_first_approver.txt')
|
||||
template_vars = {'primary_investigator': primary_investigator, 'approver_2': approver_2}
|
||||
msg.body = template.render(template_vars)
|
||||
template = env.get_template('ramp_up_denied_first_approver.html')
|
||||
msg.html = template.render(template_vars)
|
||||
|
||||
mail.send(msg)
|
||||
except Exception as e:
|
||||
return str(e)
|
|
@ -0,0 +1 @@
|
|||
<p>A Research Ramp-up approval request from {{ primary_investigator }} is now available for your review in your [Research Ramp-up Toolkit](https://rrt.uvadcos.io/app/approvals).</p>
|
|
@ -0,0 +1 @@
|
|||
A Research Ramp-up approval request from {{ primary_investigator }} is now available for your review in your [Research Ramp-up Toolkit](https://rrt.uvadcos.io/app/approvals).
|
|
@ -0,0 +1 @@
|
|||
<p>A Research Ramp-up approval request from {{ primary_investigator }} and is now available for your review in your [Research Ramp-up Toolkit](https://rrt.uvadcos.io/app/approvals).</p>
|
|
@ -0,0 +1 @@
|
|||
A Research Ramp-up approval request from {{ primary_investigator }} is now available for your review in your [Research Ramp-up Toolkit](https://rrt.uvadcos.io/app/approvals).
|
|
@ -0,0 +1 @@
|
|||
<p>Your Research Ramp-up Plan has been approved by {{ approver_1 }} {% if approver_2 %}and {{ approver_2 }} {% endif %}</p>
|
|
@ -0,0 +1 @@
|
|||
Your Research Ramp-up Plan has been approved by {{ approver_1 }} {% if approver_2 %}and {{ approver_2 }} {% endif %}
|
|
@ -0,0 +1 @@
|
|||
<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>
|
|
@ -0,0 +1 @@
|
|||
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.
|
|
@ -0,0 +1 @@
|
|||
<p>The Research Ramp-up Plan submitted by {{ primary_investigator }} was denied by {{ approver_2 }} and returned for requested updates. You may see comments related to this denial in on your Research Ramp-up Toolkit Approval dashboard.</p>
|
|
@ -0,0 +1 @@
|
|||
The Research Ramp-up Plan submitted by {{ primary_investigator }} was denied by {{ approver_2 }} and returned for requested updates. You may see comments related to this denial in on your Research Ramp-up Toolkit Approval dashboard.
|
|
@ -0,0 +1,5 @@
|
|||
<p>Your Research Ramp-up Plan (RRP) has been submitted for review by {{ approver_1 }} {% if approver_2 %}and {{ approver_2 }} {% endif %}. After completion of the review step you will receive email notification of its approval or if additional information and/or modifications are required, along with instructions on how to proceed. Return to the Research Ramp-up Plan application to proceed as instructed.</p>
|
||||
|
||||
<p>In the meantime, please make sure all required training has been completed and needed supplies secured. You will be asked to confirm that both of these requirements have been met before reopening the research space approved in your RRP.</p>
|
||||
|
||||
<p>Additionally, if there are any unknown Area Monitors for the spaces listed in your RRP, please contact your approvers to determine either who they are or how you can find out. Missing Area Monitors will need to be entered before proceeding as well.</p>
|
|
@ -0,0 +1,5 @@
|
|||
Your Research Ramp-up Plan (RRP) has been submitted for review by {{ approver_1 }} {% if approver_2 %}and {{ approver_2 }} {% endif %}. After completion of the review step you will receive email notification of its approval or if additional information and/or modifications are required, along with instructions on how to proceed. Return to the Research Ramp-up Plan application to proceed as instructed.
|
||||
|
||||
In the meantime, please make sure all required training has been completed and needed supplies secured. You will be asked to confirm that both of these requirements have been met before reopening the research space approved in your RRP.
|
||||
|
||||
Additionally, if there are any unknown Area Monitors for the spaces listed in your RRP, please contact your approvers to determine either who they are or how you can find out. Missing Area Monitors will need to be entered before proceeding as well.
|
|
@ -119,7 +119,6 @@ class BaseTest(unittest.TestCase):
|
|||
"""use_crc_data will cause this to load the mammoth collection of documents
|
||||
we built up developing crc, use_rrt_data will do the same for hte rrt project,
|
||||
otherwise it depends on a small setup for running tests."""
|
||||
|
||||
from example_data import ExampleDataLoader
|
||||
ExampleDataLoader.clean_db()
|
||||
if use_crc_data:
|
||||
|
@ -228,7 +227,7 @@ class BaseTest(unittest.TestCase):
|
|||
if study is None:
|
||||
user = self.create_user(uid=uid)
|
||||
study = StudyModel(title=title, protocol_builder_status=ProtocolBuilderStatus.ACTIVE,
|
||||
user_uid=user.uid)
|
||||
user_uid=user.uid, primary_investigator_id='lb3dp')
|
||||
db.session.add(study)
|
||||
db.session.commit()
|
||||
return study
|
||||
|
|
|
@ -68,16 +68,15 @@ class TestApprovals(BaseTest):
|
|||
approval = response[0]
|
||||
self.assertEqual(approval['approver']['uid'], approver_uid)
|
||||
|
||||
def test_list_approvals_per_admin(self):
|
||||
"""All approvals will be returned"""
|
||||
rv = self.app.get('/v1.0/approval?everything=true', headers=self.logged_in_headers())
|
||||
def test_list_approvals_as_user(self):
|
||||
"""All approvals as different user"""
|
||||
rv = self.app.get('/v1.0/approval?as_user=lb3dp', headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
|
||||
response = json.loads(rv.get_data(as_text=True))
|
||||
|
||||
# Returned approvals should match what's in the db, we should get one approval back
|
||||
# per study (2 studies), and that approval should have one related approval.
|
||||
approvals_count = ApprovalModel.query.count()
|
||||
# Returned approvals should match what's in the db for user ld3dp, we should get one
|
||||
# approval back per study (2 studies), and that approval should have one related approval.
|
||||
response_count = len(response)
|
||||
self.assertEqual(2, response_count)
|
||||
|
||||
|
@ -106,7 +105,7 @@ class TestApprovals(BaseTest):
|
|||
def test_accept_approval(self):
|
||||
approval = session.query(ApprovalModel).filter_by(approver_uid='dhf8r').first()
|
||||
data = {'id': approval.id,
|
||||
"approver_uid": "dhf8r",
|
||||
"approver": {"uid": "dhf8r"},
|
||||
'message': "Approved. I like the cut of your jib.",
|
||||
'status': ApprovalStatus.APPROVED.value}
|
||||
|
||||
|
@ -127,7 +126,7 @@ class TestApprovals(BaseTest):
|
|||
def test_decline_approval(self):
|
||||
approval = session.query(ApprovalModel).filter_by(approver_uid='dhf8r').first()
|
||||
data = {'id': approval.id,
|
||||
"approver_uid": "dhf8r",
|
||||
"approver": {"uid": "dhf8r"},
|
||||
'message': "Approved. I find the cut of your jib lacking.",
|
||||
'status': ApprovalStatus.DECLINED.value}
|
||||
|
||||
|
|
|
@ -15,13 +15,14 @@ class TestApprovalsService(BaseTest):
|
|||
name="anything.png", content_type="text",
|
||||
binary_data=b'5678', irb_doc_code="UVACompl_PRCAppr" )
|
||||
|
||||
|
||||
ApprovalService.add_approval(study_id=workflow.study_id, workflow_id=workflow.id, approver_uid="dhf8r")
|
||||
self.assertEquals(1, db.session.query(ApprovalModel).count())
|
||||
self.assertEqual(1, db.session.query(ApprovalModel).count())
|
||||
model = db.session.query(ApprovalModel).first()
|
||||
self.assertEquals(workflow.study_id, model.study_id)
|
||||
self.assertEquals(workflow.id, model.workflow_id)
|
||||
self.assertEquals("dhf8r", model.approver_uid)
|
||||
self.assertEquals(1, model.version)
|
||||
self.assertEqual(workflow.study_id, model.study_id)
|
||||
self.assertEqual(workflow.id, model.workflow_id)
|
||||
self.assertEqual("dhf8r", model.approver_uid)
|
||||
self.assertEqual(1, model.version)
|
||||
|
||||
def test_new_requests_dont_add_if_approval_exists_for_current_workflow(self):
|
||||
self.create_reference_document()
|
||||
|
@ -32,9 +33,9 @@ class TestApprovalsService(BaseTest):
|
|||
|
||||
ApprovalService.add_approval(study_id=workflow.study_id, workflow_id=workflow.id, approver_uid="dhf8r")
|
||||
ApprovalService.add_approval(study_id=workflow.study_id, workflow_id=workflow.id, approver_uid="dhf8r")
|
||||
self.assertEquals(1, db.session.query(ApprovalModel).count())
|
||||
self.assertEqual(1, db.session.query(ApprovalModel).count())
|
||||
model = db.session.query(ApprovalModel).first()
|
||||
self.assertEquals(1, model.version)
|
||||
self.assertEqual(1, model.version)
|
||||
|
||||
def test_new_approval_requests_after_file_modification_create_new_requests(self):
|
||||
self.load_example_data()
|
||||
|
@ -51,9 +52,20 @@ class TestApprovalsService(BaseTest):
|
|||
binary_data=b'5678', irb_doc_code="UVACompl_PRCAppr")
|
||||
|
||||
ApprovalService.add_approval(study_id=workflow.study_id, workflow_id=workflow.id, approver_uid="dhf8r")
|
||||
self.assertEquals(2, db.session.query(ApprovalModel).count())
|
||||
self.assertEqual(2, db.session.query(ApprovalModel).count())
|
||||
models = db.session.query(ApprovalModel).order_by(ApprovalModel.version).all()
|
||||
self.assertEquals(1, models[0].version)
|
||||
self.assertEquals(2, models[1].version)
|
||||
self.assertEqual(1, models[0].version)
|
||||
self.assertEqual(2, models[1].version)
|
||||
|
||||
def test_new_approval_sends_proper_emails(self):
|
||||
self.assertEqual(1, 1)
|
||||
|
||||
def test_new_approval_failed_ldap_lookup(self):
|
||||
# failed lookup should send email to sartographysupport@googlegroups.com + Cheryl
|
||||
self.assertEqual(1, 1)
|
||||
|
||||
def test_approve_approval_sends_proper_emails(self):
|
||||
self.assertEqual(1, 1)
|
||||
|
||||
def test_deny_approval_sends_proper_emails(self):
|
||||
self.assertEqual(1, 1)
|
||||
|
|
|
@ -49,10 +49,10 @@ class TestAuthentication(BaseTest):
|
|||
self.assert_success(rv)
|
||||
user = db.session.query(UserModel).filter(UserModel.uid == new_uid).first()
|
||||
self.assertIsNotNone(user)
|
||||
self.assertEquals(new_uid, user.uid)
|
||||
self.assertEquals("Laura Barnes", user.display_name)
|
||||
self.assertEquals("lb3dp@virginia.edu", user.email_address)
|
||||
self.assertEquals("E0:Associate Professor of Systems and Information Engineering", user.title)
|
||||
self.assertEqual(new_uid, user.uid)
|
||||
self.assertEqual("Laura Barnes", user.display_name)
|
||||
self.assertEqual("lb3dp@virginia.edu", user.email_address)
|
||||
self.assertEqual("E0:Associate Professor of Systems and Information Engineering", user.title)
|
||||
|
||||
|
||||
def test_current_user_status(self):
|
||||
|
|
|
@ -17,7 +17,7 @@ class TestCompleteTemplate(unittest.TestCase):
|
|||
data = {"name": "Dan"}
|
||||
data_copy = copy.deepcopy(data)
|
||||
script.rich_text_update(data_copy)
|
||||
self.assertEquals(data, data_copy)
|
||||
self.assertEqual(data, data_copy)
|
||||
|
||||
def test_rich_text_update_new_line(self):
|
||||
script = CompleteTemplate()
|
||||
|
|
|
@ -23,11 +23,11 @@ class TestFileService(BaseTest):
|
|||
binary_data=b'5678', irb_doc_code=irb_code)
|
||||
|
||||
file_models = FileService.get_workflow_files(workflow_id=workflow.id)
|
||||
self.assertEquals(1, len(file_models))
|
||||
self.assertEqual(1, len(file_models))
|
||||
|
||||
file_data = FileService.get_workflow_data_files(workflow_id=workflow.id)
|
||||
self.assertEquals(1, len(file_data))
|
||||
self.assertEquals(2, file_data[0].version)
|
||||
self.assertEqual(1, len(file_data))
|
||||
self.assertEqual(2, file_data[0].version)
|
||||
|
||||
|
||||
def test_add_file_from_form_increments_version_and_replaces_on_subsequent_add_with_same_name(self):
|
||||
|
@ -77,12 +77,13 @@ class TestFileService(BaseTest):
|
|||
binary_data=b'5678')
|
||||
|
||||
file_models = FileService.get_workflow_files(workflow_id=workflow.id)
|
||||
self.assertEquals(1, len(file_models))
|
||||
self.assertEqual(1, len(file_models))
|
||||
|
||||
file_data = FileService.get_workflow_data_files(workflow_id=workflow.id)
|
||||
self.assertEquals(1, len(file_data))
|
||||
self.assertEquals(2, file_data[0].version)
|
||||
self.assertEquals(b'5678', file_data[0].data)
|
||||
|
||||
self.assertEqual(1, len(file_data))
|
||||
self.assertEqual(2, file_data[0].version)
|
||||
self.assertEqual(b'5678', file_data[0].data)
|
||||
|
||||
def test_add_file_from_form_allows_multiple_files_with_different_names(self):
|
||||
self.load_example_data()
|
||||
|
@ -101,4 +102,4 @@ class TestFileService(BaseTest):
|
|||
name="a_different_thing.png", content_type="text",
|
||||
binary_data=b'5678')
|
||||
file_models = FileService.get_workflow_files(workflow_id=workflow.id)
|
||||
self.assertEquals(2, len(file_models))
|
||||
self.assertEqual(2, len(file_models))
|
||||
|
|
|
@ -30,4 +30,4 @@ class TestLdapService(BaseTest):
|
|||
user_info = LdapService.user_info("nosuch")
|
||||
self.assertFalse(True, "An API error should be raised.")
|
||||
except ApiError as ae:
|
||||
self.assertEquals("missing_ldap_record", ae.code)
|
||||
self.assertEqual("missing_ldap_record", ae.code)
|
|
@ -31,7 +31,7 @@ class TestLookupService(BaseTest):
|
|||
self.assertEqual(1, len(lookup_records))
|
||||
lookup_record = lookup_records[0]
|
||||
lookup_data = session.query(LookupDataModel).filter(LookupDataModel.lookup_file_model == lookup_record).all()
|
||||
self.assertEquals(28, len(lookup_data))
|
||||
self.assertEqual(28, len(lookup_data))
|
||||
|
||||
def test_updates_to_file_cause_lookup_rebuild(self):
|
||||
spec = BaseTest.load_test_spec('enum_options_with_search')
|
||||
|
@ -43,7 +43,7 @@ class TestLookupService(BaseTest):
|
|||
self.assertEqual(1, len(lookup_records))
|
||||
lookup_record = lookup_records[0]
|
||||
lookup_data = session.query(LookupDataModel).filter(LookupDataModel.lookup_file_model == lookup_record).all()
|
||||
self.assertEquals(28, len(lookup_data))
|
||||
self.assertEqual(28, len(lookup_data))
|
||||
|
||||
# Update the workflow specification file.
|
||||
file_path = os.path.join(app.root_path, '..', 'tests', 'data',
|
||||
|
@ -59,7 +59,7 @@ class TestLookupService(BaseTest):
|
|||
lookup_records = session.query(LookupFileModel).all()
|
||||
lookup_record = lookup_records[0]
|
||||
lookup_data = session.query(LookupDataModel).filter(LookupDataModel.lookup_file_model == lookup_record).all()
|
||||
self.assertEquals(4, len(lookup_data))
|
||||
self.assertEqual(4, len(lookup_data))
|
||||
|
||||
|
||||
|
||||
|
@ -70,49 +70,49 @@ class TestLookupService(BaseTest):
|
|||
processor.do_engine_steps()
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "", limit=10)
|
||||
self.assertEquals(10, len(results), "Blank queries return everything, to the limit")
|
||||
self.assertEqual(10, len(results), "Blank queries return everything, to the limit")
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "medicines", limit=10)
|
||||
self.assertEquals(1, len(results), "words in the middle of label are detected.")
|
||||
self.assertEquals("The Medicines Company", results[0].label)
|
||||
self.assertEqual(1, len(results), "words in the middle of label are detected.")
|
||||
self.assertEqual("The Medicines Company", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "UVA", limit=10)
|
||||
self.assertEquals(1, len(results), "Beginning of label is found.")
|
||||
self.assertEquals("UVA - INTERNAL - GM USE ONLY", results[0].label)
|
||||
self.assertEqual(1, len(results), "Beginning of label is found.")
|
||||
self.assertEqual("UVA - INTERNAL - GM USE ONLY", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "uva", limit=10)
|
||||
self.assertEquals(1, len(results), "case does not matter.")
|
||||
self.assertEquals("UVA - INTERNAL - GM USE ONLY", results[0].label)
|
||||
self.assertEqual(1, len(results), "case does not matter.")
|
||||
self.assertEqual("UVA - INTERNAL - GM USE ONLY", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "medici", limit=10)
|
||||
self.assertEquals(1, len(results), "partial words are picked up.")
|
||||
self.assertEquals("The Medicines Company", results[0].label)
|
||||
self.assertEqual(1, len(results), "partial words are picked up.")
|
||||
self.assertEqual("The Medicines Company", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "Genetics Savings", limit=10)
|
||||
self.assertEquals(1, len(results), "multiple terms are picked up..")
|
||||
self.assertEquals("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
self.assertEqual(1, len(results), "multiple terms are picked up..")
|
||||
self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "Genetics Sav", limit=10)
|
||||
self.assertEquals(1, len(results), "prefix queries still work with partial terms")
|
||||
self.assertEquals("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
self.assertEqual(1, len(results), "prefix queries still work with partial terms")
|
||||
self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "Gen Sav", limit=10)
|
||||
self.assertEquals(1, len(results), "prefix queries still work with ALL the partial terms")
|
||||
self.assertEquals("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
self.assertEqual(1, len(results), "prefix queries still work with ALL the partial terms")
|
||||
self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "Inc", limit=10)
|
||||
self.assertEquals(7, len(results), "short terms get multiple correct results.")
|
||||
self.assertEquals("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
self.assertEqual(7, len(results), "short terms get multiple correct results.")
|
||||
self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "reaction design", limit=10)
|
||||
self.assertEquals(5, len(results), "all results come back for two terms.")
|
||||
self.assertEquals("Reaction Design", results[0].label, "Exact matches come first.")
|
||||
self.assertEqual(5, len(results), "all results come back for two terms.")
|
||||
self.assertEqual("Reaction Design", results[0].label, "Exact matches come first.")
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "1 Something", limit=10)
|
||||
self.assertEquals("1 Something", results[0].label, "Exact matches are prefered")
|
||||
self.assertEqual("1 Something", results[0].label, "Exact matches are prefered")
|
||||
|
||||
results = LookupService.lookup(workflow, "AllTheNames", "1 (!-Something", limit=10)
|
||||
self.assertEquals("1 Something", results[0].label, "special characters don't flake out")
|
||||
self.assertEqual("1 Something", results[0].label, "special characters don't flake out")
|
||||
|
||||
|
||||
|
||||
|
@ -124,6 +124,6 @@ class TestLookupService(BaseTest):
|
|||
|
||||
# Fixme: Stop words are taken into account on the query side, and haven't found a fix yet.
|
||||
#results = WorkflowService.run_lookup_query(lookup_table.id, "in", limit=10)
|
||||
#self.assertEquals(7, len(results), "stop words are not removed.")
|
||||
#self.assertEquals("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
#self.assertEqual(7, len(results), "stop words are not removed.")
|
||||
#self.assertEqual("Genetics Savings & Clone, Inc.", results[0].label)
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
from crc.services.mails import (
|
||||
send_ramp_up_submission_email,
|
||||
send_ramp_up_approval_request_email,
|
||||
send_ramp_up_approval_request_first_review_email,
|
||||
send_ramp_up_approved_email,
|
||||
send_ramp_up_denied_email,
|
||||
send_ramp_up_denied_email_to_approver
|
||||
)
|
||||
|
||||
|
||||
class TestMails(BaseTest):
|
||||
|
||||
def setUp(self):
|
||||
self.sender = 'sender@sartography.com'
|
||||
self.recipients = ['recipient@sartography.com']
|
||||
self.primary_investigator = 'Dr. Bartlett'
|
||||
self.approver_1 = 'Max Approver'
|
||||
self.approver_2 = 'Close Reviewer'
|
||||
|
||||
def test_send_ramp_up_submission_email(self):
|
||||
send_ramp_up_submission_email(self.sender, self.recipients, self.approver_1)
|
||||
self.assertTrue(True)
|
||||
|
||||
send_ramp_up_submission_email(self.sender, self.recipients, self.approver_1, self.approver_2)
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_send_ramp_up_approval_request_email(self):
|
||||
send_ramp_up_approval_request_email(self.sender, self.recipients, self.primary_investigator)
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_send_ramp_up_approval_request_first_review_email(self):
|
||||
send_ramp_up_approval_request_first_review_email(
|
||||
self.sender, self.recipients, self.primary_investigator
|
||||
)
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_send_ramp_up_approved_email(self):
|
||||
send_ramp_up_approved_email(self.sender, self.recipients, self.approver_1)
|
||||
self.assertTrue(True)
|
||||
|
||||
send_ramp_up_approved_email(self.sender, self.recipients, self.approver_1, self.approver_2)
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_send_ramp_up_denied_email(self):
|
||||
send_ramp_up_denied_email(self.sender, self.recipients, self.approver_1)
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_send_send_ramp_up_denied_email_to_approver(self):
|
||||
send_ramp_up_denied_email_to_approver(
|
||||
self.sender, self.recipients, self.primary_investigator, self.approver_2
|
||||
)
|
||||
self.assertTrue(True)
|
|
@ -24,7 +24,7 @@ class TestRequestApprovalScript(BaseTest):
|
|||
binary_data=b'1234')
|
||||
script = RequestApproval()
|
||||
script.do_task(task, workflow.study_id, workflow.id, "study.approval1", "study.approval2")
|
||||
self.assertEquals(2, db.session.query(ApprovalModel).count())
|
||||
self.assertEqual(2, db.session.query(ApprovalModel).count())
|
||||
|
||||
def test_do_task_with_blank_second_approver(self):
|
||||
self.load_example_data()
|
||||
|
@ -39,7 +39,7 @@ class TestRequestApprovalScript(BaseTest):
|
|||
binary_data=b'1234')
|
||||
script = RequestApproval()
|
||||
script.do_task(task, workflow.study_id, workflow.id, "study.approval1", "study.approval2")
|
||||
self.assertEquals(1, db.session.query(ApprovalModel).count())
|
||||
self.assertEqual(1, db.session.query(ApprovalModel).count())
|
||||
|
||||
|
||||
def test_do_task_with_incorrect_argument(self):
|
||||
|
@ -64,5 +64,5 @@ class TestRequestApprovalScript(BaseTest):
|
|||
|
||||
script = RequestApproval()
|
||||
script.do_task_validate_only(task, workflow.study_id, workflow.id, "study.approval1")
|
||||
self.assertEquals(0, db.session.query(ApprovalModel).count())
|
||||
self.assertEqual(0, db.session.query(ApprovalModel).count())
|
||||
|
||||
|
|
|
@ -87,10 +87,10 @@ class TestStudyApi(BaseTest):
|
|||
headers=self.logged_in_headers(), content_type="application/json")
|
||||
self.assert_success(api_response)
|
||||
study = StudySchema().loads(api_response.get_data(as_text=True))
|
||||
self.assertEquals(1, len(study.files))
|
||||
self.assertEquals("UVA Compliance/PRC Approval", study.files[0]["category"])
|
||||
self.assertEquals("Cancer Center's PRC Approval Form", study.files[0]["description"])
|
||||
self.assertEquals("UVA Compliance/PRC Approval.png", study.files[0]["download_name"])
|
||||
self.assertEqual(1, len(study.files))
|
||||
self.assertEqual("UVA Compliance/PRC Approval", study.files[0]["category"])
|
||||
self.assertEqual("Cancer Center's PRC Approval Form", study.files[0]["description"])
|
||||
self.assertEqual("UVA Compliance/PRC Approval.png", study.files[0]["download_name"])
|
||||
|
||||
# TODO: WRITE A TEST FOR STUDY FILES
|
||||
|
||||
|
@ -180,10 +180,10 @@ class TestStudyApi(BaseTest):
|
|||
db_studies_after = session.query(StudyModel).all()
|
||||
num_db_studies_after = len(db_studies_after)
|
||||
self.assertGreater(num_db_studies_after, num_db_studies_before)
|
||||
self.assertEquals(num_abandoned, 1)
|
||||
self.assertEquals(num_open, 1)
|
||||
self.assertEquals(num_active, 1)
|
||||
self.assertEquals(num_incomplete, 1)
|
||||
self.assertEqual(num_abandoned, 1)
|
||||
self.assertEqual(num_open, 1)
|
||||
self.assertEqual(num_active, 1)
|
||||
self.assertEqual(num_incomplete, 1)
|
||||
self.assertEqual(len(json_data), num_db_studies_after)
|
||||
self.assertEqual(num_open + num_active + num_incomplete + num_abandoned, num_db_studies_after)
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ class TestStudyService(BaseTest):
|
|||
self.assertEqual(1, docs["UVACompl_PRCAppr"]['count'])
|
||||
self.assertIsNotNone(docs["UVACompl_PRCAppr"]['files'][0])
|
||||
self.assertIsNotNone(docs["UVACompl_PRCAppr"]['files'][0]['file_id'])
|
||||
self.assertEquals(workflow.id, docs["UVACompl_PRCAppr"]['files'][0]['workflow_id'])
|
||||
self.assertEqual(workflow.id, docs["UVACompl_PRCAppr"]['files'][0]['workflow_id'])
|
||||
|
||||
def test_get_all_studies(self):
|
||||
user = self.create_user_with_study_and_workflow()
|
||||
|
@ -174,8 +174,8 @@ class TestStudyService(BaseTest):
|
|||
binary_data=b'1234', irb_doc_code="UVACompl_PRCAppr" )
|
||||
|
||||
studies = StudyService().get_all_studies_with_files()
|
||||
self.assertEquals(1, len(studies))
|
||||
self.assertEquals(3, len(studies[0].files))
|
||||
self.assertEqual(1, len(studies))
|
||||
self.assertEqual(3, len(studies[0].files))
|
||||
|
||||
|
||||
|
||||
|
@ -191,17 +191,17 @@ class TestStudyService(BaseTest):
|
|||
workflow = self.create_workflow('docx') # The workflow really doesnt matter in this case.
|
||||
investigators = StudyService().get_investigators(workflow.study_id)
|
||||
|
||||
self.assertEquals(9, len(investigators))
|
||||
self.assertEqual(9, len(investigators))
|
||||
|
||||
# dhf8r is in the ldap mock data.
|
||||
self.assertEquals("dhf8r", investigators['PI']['user_id'])
|
||||
self.assertEquals("Dan Funk", investigators['PI']['display_name']) # Data from ldap
|
||||
self.assertEquals("Primary Investigator", investigators['PI']['label']) # Data from xls file.
|
||||
self.assertEquals("Always", investigators['PI']['display']) # Data from xls file.
|
||||
self.assertEqual("dhf8r", investigators['PI']['user_id'])
|
||||
self.assertEqual("Dan Funk", investigators['PI']['display_name']) # Data from ldap
|
||||
self.assertEqual("Primary Investigator", investigators['PI']['label']) # Data from xls file.
|
||||
self.assertEqual("Always", investigators['PI']['display']) # Data from xls file.
|
||||
|
||||
# asd3v is not in ldap, so an error should be returned.
|
||||
self.assertEquals("asd3v", investigators['DC']['user_id'])
|
||||
self.assertEquals("Unable to locate a user with id asd3v in LDAP", investigators['DC']['error']) # Data from ldap
|
||||
self.assertEqual("asd3v", investigators['DC']['user_id'])
|
||||
self.assertEqual("Unable to locate a user with id asd3v in LDAP", investigators['DC']['error']) # Data from ldap
|
||||
|
||||
# No value is provided for Department Chair
|
||||
self.assertIsNone(investigators['DEPT_CH']['user_id'])
|
||||
|
|
|
@ -48,7 +48,7 @@ class TestTasksApi(BaseTest):
|
|||
# The total number of tasks may change over time, as users move through gateways
|
||||
# branches may be pruned. As we hit parallel Multi-Instance new tasks may be created...
|
||||
self.assertIsNotNone(workflow.total_tasks)
|
||||
self.assertEquals(prev_completed_task_count + 1, workflow.completed_tasks)
|
||||
self.assertEqual(prev_completed_task_count + 1, workflow.completed_tasks)
|
||||
# Assure a record exists in the Task Events
|
||||
task_events = session.query(TaskEventModel) \
|
||||
.filter_by(workflow_id=workflow.id) \
|
||||
|
@ -57,25 +57,25 @@ class TestTasksApi(BaseTest):
|
|||
self.assertGreater(len(task_events), 0)
|
||||
event = task_events[0]
|
||||
self.assertIsNotNone(event.study_id)
|
||||
self.assertEquals("dhf8r", event.user_uid)
|
||||
self.assertEquals(workflow.id, event.workflow_id)
|
||||
self.assertEquals(workflow.workflow_spec_id, event.workflow_spec_id)
|
||||
self.assertEquals(workflow.spec_version, event.spec_version)
|
||||
self.assertEquals(WorkflowService.TASK_ACTION_COMPLETE, event.action)
|
||||
self.assertEquals(task_in.id, task_id)
|
||||
self.assertEquals(task_in.name, event.task_name)
|
||||
self.assertEquals(task_in.title, event.task_title)
|
||||
self.assertEquals(task_in.type, event.task_type)
|
||||
self.assertEquals("COMPLETED", event.task_state)
|
||||
self.assertEqual("dhf8r", event.user_uid)
|
||||
self.assertEqual(workflow.id, event.workflow_id)
|
||||
self.assertEqual(workflow.workflow_spec_id, event.workflow_spec_id)
|
||||
self.assertEqual(workflow.spec_version, event.spec_version)
|
||||
self.assertEqual(WorkflowService.TASK_ACTION_COMPLETE, event.action)
|
||||
self.assertEqual(task_in.id, task_id)
|
||||
self.assertEqual(task_in.name, event.task_name)
|
||||
self.assertEqual(task_in.title, event.task_title)
|
||||
self.assertEqual(task_in.type, event.task_type)
|
||||
self.assertEqual("COMPLETED", event.task_state)
|
||||
# Not sure what vodoo is happening inside of marshmallow to get me in this state.
|
||||
if isinstance(task_in.multi_instance_type, MultiInstanceType):
|
||||
self.assertEquals(task_in.multi_instance_type.value, event.mi_type)
|
||||
self.assertEqual(task_in.multi_instance_type.value, event.mi_type)
|
||||
else:
|
||||
self.assertEquals(task_in.multi_instance_type, event.mi_type)
|
||||
self.assertEqual(task_in.multi_instance_type, event.mi_type)
|
||||
|
||||
self.assertEquals(task_in.multi_instance_count, event.mi_count)
|
||||
self.assertEquals(task_in.multi_instance_index, event.mi_index)
|
||||
self.assertEquals(task_in.process_name, event.process_name)
|
||||
self.assertEqual(task_in.multi_instance_count, event.mi_count)
|
||||
self.assertEqual(task_in.multi_instance_index, event.mi_index)
|
||||
self.assertEqual(task_in.process_name, event.process_name)
|
||||
self.assertIsNotNone(event.date)
|
||||
|
||||
|
||||
|
@ -155,14 +155,14 @@ class TestTasksApi(BaseTest):
|
|||
|
||||
self.assertIsNotNone(workflow_api.navigation)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEquals(5, len(nav))
|
||||
self.assertEquals("Do You Have Bananas", nav[0]['title'])
|
||||
self.assertEquals("Bananas?", nav[1]['title'])
|
||||
self.assertEquals("FUTURE", nav[1]['state'])
|
||||
self.assertEquals("yes", nav[2]['title'])
|
||||
self.assertEquals("NOOP", nav[2]['state'])
|
||||
self.assertEquals("no", nav[3]['title'])
|
||||
self.assertEquals("NOOP", nav[3]['state'])
|
||||
self.assertEqual(5, len(nav))
|
||||
self.assertEqual("Do You Have Bananas", nav[0]['title'])
|
||||
self.assertEqual("Bananas?", nav[1]['title'])
|
||||
self.assertEqual("FUTURE", nav[1]['state'])
|
||||
self.assertEqual("yes", nav[2]['title'])
|
||||
self.assertEqual("NOOP", nav[2]['state'])
|
||||
self.assertEqual("no", nav[3]['title'])
|
||||
self.assertEqual("NOOP", nav[3]['state'])
|
||||
|
||||
def test_navigation_with_exclusive_gateway(self):
|
||||
self.load_example_data()
|
||||
|
@ -172,14 +172,14 @@ class TestTasksApi(BaseTest):
|
|||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertIsNotNone(workflow_api.navigation)
|
||||
nav = workflow_api.navigation
|
||||
self.assertEquals(7, len(nav))
|
||||
self.assertEquals("Task 1", nav[0]['title'])
|
||||
self.assertEquals("Which Branch?", nav[1]['title'])
|
||||
self.assertEquals("a", nav[2]['title'])
|
||||
self.assertEquals("Task 2a", nav[3]['title'])
|
||||
self.assertEquals("b", nav[4]['title'])
|
||||
self.assertEquals("Task 2b", nav[5]['title'])
|
||||
self.assertEquals("Task 3", nav[6]['title'])
|
||||
self.assertEqual(7, len(nav))
|
||||
self.assertEqual("Task 1", nav[0]['title'])
|
||||
self.assertEqual("Which Branch?", nav[1]['title'])
|
||||
self.assertEqual("a", nav[2]['title'])
|
||||
self.assertEqual("Task 2a", nav[3]['title'])
|
||||
self.assertEqual("b", nav[4]['title'])
|
||||
self.assertEqual("Task 2b", nav[5]['title'])
|
||||
self.assertEqual("Task 3", nav[6]['title'])
|
||||
|
||||
def test_document_added_to_workflow_shows_up_in_file_list(self):
|
||||
self.load_example_data()
|
||||
|
@ -285,8 +285,8 @@ class TestTasksApi(BaseTest):
|
|||
workflow_api = self.complete_form(workflow, task, {"name": "Dan"})
|
||||
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
self.assertEquals('Task_Manual_One', workflow.next_task.name)
|
||||
self.assertEquals('ManualTask', workflow_api.next_task.type)
|
||||
self.assertEqual('Task_Manual_One', workflow.next_task.name)
|
||||
self.assertEqual('ManualTask', workflow_api.next_task.type)
|
||||
self.assertTrue('Markdown' in workflow_api.next_task.documentation)
|
||||
self.assertTrue('Dan' in workflow_api.next_task.documentation)
|
||||
|
||||
|
@ -296,7 +296,7 @@ class TestTasksApi(BaseTest):
|
|||
|
||||
# get the first form in the two form workflow.
|
||||
task = self.get_workflow_api(workflow).next_task
|
||||
self.assertEquals("JustAValue", task.properties['JustAKey'])
|
||||
self.assertEqual("JustAValue", task.properties['JustAKey'])
|
||||
|
||||
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
|
@ -316,13 +316,13 @@ class TestTasksApi(BaseTest):
|
|||
# get the first form in the two form workflow.
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
navigation = self.get_workflow_api(workflow).navigation
|
||||
self.assertEquals(4, len(navigation)) # Start task, form_task, multi_task, end task
|
||||
self.assertEquals("UserTask", workflow.next_task.type)
|
||||
self.assertEquals(MultiInstanceType.sequential.value, workflow.next_task.multi_instance_type)
|
||||
self.assertEquals(9, workflow.next_task.multi_instance_count)
|
||||
self.assertEqual(4, len(navigation)) # Start task, form_task, multi_task, end task
|
||||
self.assertEqual("UserTask", workflow.next_task.type)
|
||||
self.assertEqual(MultiInstanceType.sequential.value, workflow.next_task.multi_instance_type)
|
||||
self.assertEqual(9, workflow.next_task.multi_instance_count)
|
||||
|
||||
# Assure that the names for each task are properly updated, so they aren't all the same.
|
||||
self.assertEquals("Primary Investigator", workflow.next_task.properties['display_name'])
|
||||
self.assertEqual("Primary Investigator", workflow.next_task.properties['display_name'])
|
||||
|
||||
|
||||
def test_lookup_endpoint_for_task_field_enumerations(self):
|
||||
|
@ -364,18 +364,18 @@ class TestTasksApi(BaseTest):
|
|||
navigation = workflow_api.navigation
|
||||
task = workflow_api.next_task
|
||||
|
||||
self.assertEquals(2, len(navigation))
|
||||
self.assertEquals("UserTask", task.type)
|
||||
self.assertEquals("Activity_A", task.name)
|
||||
self.assertEquals("My Sub Process", task.process_name)
|
||||
self.assertEqual(2, len(navigation))
|
||||
self.assertEqual("UserTask", task.type)
|
||||
self.assertEqual("Activity_A", task.name)
|
||||
self.assertEqual("My Sub Process", task.process_name)
|
||||
workflow_api = self.complete_form(workflow, task, {"name": "Dan"})
|
||||
task = workflow_api.next_task
|
||||
self.assertIsNotNone(task)
|
||||
|
||||
self.assertEquals("Activity_B", task.name)
|
||||
self.assertEquals("Sub Workflow Example", task.process_name)
|
||||
self.assertEqual("Activity_B", task.name)
|
||||
self.assertEqual("Sub Workflow Example", task.process_name)
|
||||
workflow_api = self.complete_form(workflow, task, {"name": "Dan"})
|
||||
self.assertEquals(WorkflowStatus.complete, workflow_api.status)
|
||||
self.assertEqual(WorkflowStatus.complete, workflow_api.status)
|
||||
|
||||
def test_update_task_resets_token(self):
|
||||
self.load_example_data()
|
||||
|
@ -385,7 +385,7 @@ class TestTasksApi(BaseTest):
|
|||
first_task = self.get_workflow_api(workflow).next_task
|
||||
self.complete_form(workflow, first_task, {"has_bananas": True})
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
self.assertEquals('Task_Num_Bananas', workflow.next_task.name)
|
||||
self.assertEqual('Task_Num_Bananas', workflow.next_task.name)
|
||||
|
||||
# Trying to re-submit the initial task, and answer differently, should result in an error.
|
||||
self.complete_form(workflow, first_task, {"has_bananas": False}, error_code="invalid_state")
|
||||
|
@ -406,18 +406,18 @@ class TestTasksApi(BaseTest):
|
|||
workflow = WorkflowApiSchema().load(json_data)
|
||||
|
||||
# Assure the Next Task is the one we just reset the token to be on.
|
||||
self.assertEquals("Task_Has_Bananas", workflow.next_task.name)
|
||||
self.assertEqual("Task_Has_Bananas", workflow.next_task.name)
|
||||
|
||||
# Go ahead and get that workflow one more time, it should still be right.
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
|
||||
# Assure the Next Task is the one we just reset the token to be on.
|
||||
self.assertEquals("Task_Has_Bananas", workflow.next_task.name)
|
||||
self.assertEqual("Task_Has_Bananas", workflow.next_task.name)
|
||||
|
||||
# The next task should be a different value.
|
||||
self.complete_form(workflow, workflow.next_task, {"has_bananas": False})
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
self.assertEquals('Task_Why_No_Bananas', workflow.next_task.name)
|
||||
self.assertEqual('Task_Why_No_Bananas', workflow.next_task.name)
|
||||
|
||||
@patch('crc.services.protocol_builder.requests.get')
|
||||
def test_parallel_multi_instance(self, mock_get):
|
||||
|
@ -432,13 +432,13 @@ class TestTasksApi(BaseTest):
|
|||
workflow = self.create_workflow('multi_instance_parallel')
|
||||
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEquals(12, len(workflow_api.navigation))
|
||||
self.assertEqual(12, len(workflow_api.navigation))
|
||||
ready_items = [nav for nav in workflow_api.navigation if nav['state'] == "READY"]
|
||||
self.assertEquals(9, len(ready_items))
|
||||
self.assertEqual(9, len(ready_items))
|
||||
|
||||
self.assertEquals("UserTask", workflow_api.next_task.type)
|
||||
self.assertEquals("MutiInstanceTask",workflow_api.next_task.name)
|
||||
self.assertEquals("more information", workflow_api.next_task.title)
|
||||
self.assertEqual("UserTask", workflow_api.next_task.type)
|
||||
self.assertEqual("MutiInstanceTask",workflow_api.next_task.name)
|
||||
self.assertEqual("more information", workflow_api.next_task.title)
|
||||
|
||||
for i in random.sample(range(9), 9):
|
||||
task = TaskSchema().load(ready_items[i]['task'])
|
||||
|
@ -446,5 +446,5 @@ class TestTasksApi(BaseTest):
|
|||
#tasks = self.get_workflow_api(workflow).user_tasks
|
||||
|
||||
workflow = self.get_workflow_api(workflow)
|
||||
self.assertEquals(WorkflowStatus.complete, workflow.status)
|
||||
self.assertEqual(WorkflowStatus.complete, workflow.status)
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class TestStudyApi(BaseTest):
|
|||
content_type='multipart/form-data')
|
||||
self.assert_success(rv)
|
||||
self.assertIsNotNone(rv.data)
|
||||
self.assertEquals('application/octet-stream', rv.content_type)
|
||||
self.assertEqual('application/octet-stream', rv.content_type)
|
||||
|
||||
def test_list_scripts(self):
|
||||
rv = self.app.get('/v1.0/list_scripts')
|
||||
|
|
|
@ -19,5 +19,5 @@ class TestUpdateStudyScript(BaseTest):
|
|||
|
||||
script = UpdateStudy()
|
||||
script.do_task(task, workflow.study_id, workflow.id, "title:details.label", "pi:details.value")
|
||||
self.assertEquals("My New Title", workflow.study.title)
|
||||
self.assertEquals("dhf8r", workflow.study.primary_investigator_id)
|
||||
self.assertEqual("My New Title", workflow.study.title)
|
||||
self.assertEqual("dhf8r", workflow.study.primary_investigator_id)
|
||||
|
|
|
@ -57,13 +57,13 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
|||
task = next_user_tasks[0]
|
||||
|
||||
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
|
||||
self.assertEquals("dhf8r", task.data["investigator"]["user_id"])
|
||||
self.assertEqual("dhf8r", task.data["investigator"]["user_id"])
|
||||
|
||||
self.assertEqual("MutiInstanceTask", task.get_name())
|
||||
api_task = WorkflowService.spiff_task_to_api_task(task)
|
||||
self.assertEquals(MultiInstanceType.sequential, api_task.multi_instance_type)
|
||||
self.assertEquals(3, api_task.multi_instance_count)
|
||||
self.assertEquals(1, api_task.multi_instance_index)
|
||||
self.assertEqual(MultiInstanceType.sequential, api_task.multi_instance_type)
|
||||
self.assertEqual(3, api_task.multi_instance_count)
|
||||
self.assertEqual(1, api_task.multi_instance_index)
|
||||
task.update_data({"investigator":{"email":"asd3v@virginia.edu"}})
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
|
@ -72,8 +72,8 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
|||
api_task = WorkflowService.spiff_task_to_api_task(task)
|
||||
self.assertEqual("MutiInstanceTask", api_task.name)
|
||||
task.update_data({"investigator":{"email":"asdf32@virginia.edu"}})
|
||||
self.assertEquals(3, api_task.multi_instance_count)
|
||||
self.assertEquals(2, api_task.multi_instance_index)
|
||||
self.assertEqual(3, api_task.multi_instance_count)
|
||||
self.assertEqual(2, api_task.multi_instance_index)
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
|
||||
|
@ -81,8 +81,8 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
|||
api_task = WorkflowService.spiff_task_to_api_task(task)
|
||||
self.assertEqual("MutiInstanceTask", task.get_name())
|
||||
task.update_data({"investigator":{"email":"dhf8r@virginia.edu"}})
|
||||
self.assertEquals(3, api_task.multi_instance_count)
|
||||
self.assertEquals(3, api_task.multi_instance_index)
|
||||
self.assertEqual(3, api_task.multi_instance_count)
|
||||
self.assertEqual(3, api_task.multi_instance_index)
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
task = processor.bpmn_workflow.last_task
|
||||
|
@ -91,7 +91,7 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
|||
expected['PI']['email'] = "asd3v@virginia.edu"
|
||||
expected['SC_I']['email'] = "asdf32@virginia.edu"
|
||||
expected['DC']['email'] = "dhf8r@virginia.edu"
|
||||
self.assertEquals(expected,
|
||||
self.assertEqual(expected,
|
||||
task.data['StudyInfo']['investigators'])
|
||||
|
||||
self.assertEqual(WorkflowStatus.complete, processor.get_status())
|
||||
|
@ -117,10 +117,10 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
|||
task = next_user_tasks[2]
|
||||
|
||||
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
|
||||
self.assertEquals("asd3v", task.data["investigator"]["user_id"]) # The last of the tasks
|
||||
self.assertEqual("asd3v", task.data["investigator"]["user_id"]) # The last of the tasks
|
||||
|
||||
api_task = WorkflowService.spiff_task_to_api_task(task)
|
||||
self.assertEquals(MultiInstanceType.parallel, api_task.multi_instance_type)
|
||||
self.assertEqual(MultiInstanceType.parallel, api_task.multi_instance_type)
|
||||
task.update_data({"investigator":{"email":"dhf8r@virginia.edu"}})
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
|
@ -144,7 +144,7 @@ class TestWorkflowProcessorMultiInstance(BaseTest):
|
|||
expected['PI']['email'] = "asd3v@virginia.edu"
|
||||
expected['SC_I']['email'] = "asdf32@virginia.edu"
|
||||
expected['DC']['email'] = "dhf8r@virginia.edu"
|
||||
self.assertEquals(expected,
|
||||
self.assertEqual(expected,
|
||||
task.data['StudyInfo']['investigators'])
|
||||
|
||||
self.assertEqual(WorkflowStatus.complete, processor.get_status())
|
||||
|
|
|
@ -66,9 +66,9 @@ class TestWorkflowService(BaseTest):
|
|||
task = processor.next_task()
|
||||
WorkflowService.process_options(task, task.task_spec.form.fields[0])
|
||||
options = task.task_spec.form.fields[0].options
|
||||
self.assertEquals(28, len(options))
|
||||
self.assertEquals('1000', options[0]['id'])
|
||||
self.assertEquals("UVA - INTERNAL - GM USE ONLY", options[0]['name'])
|
||||
self.assertEqual(28, len(options))
|
||||
self.assertEqual('1000', options[0]['id'])
|
||||
self.assertEqual("UVA - INTERNAL - GM USE ONLY", options[0]['name'])
|
||||
|
||||
def test_random_data_populate_form_on_auto_complete(self):
|
||||
self.load_example_data()
|
||||
|
|
Loading…
Reference in New Issue