diff --git a/portal/models/auth.py b/portal/models/auth.py index 5f52eb3172..2305f09626 100644 --- a/portal/models/auth.py +++ b/portal/models/auth.py @@ -362,6 +362,7 @@ def token_janitor(): body=body) try: em.send_message() + db.session.add(em) except SMTPRecipientsRefused as exc: msg = ("Error sending site summary email to {}: " "{}".format(sponsor_email, exc)) @@ -369,4 +370,5 @@ def token_janitor(): for email in exc[0]: error_emails.add(email) + db.session.commit() return list(error_emails) diff --git a/portal/models/qb_status.py b/portal/models/qb_status.py index 399058cb55..88cb39ec88 100644 --- a/portal/models/qb_status.py +++ b/portal/models/qb_status.py @@ -198,6 +198,9 @@ def _indef_init(self): for q in qbs: if self._current_indef is not None: raise RuntimeError("unexpected second indef qb") + if q.relative_start > self.as_of_date: + # Don't include if the consent date hasn't arrived + continue self._current_indef = q def indef_status(self): diff --git a/portal/models/reporting.py b/portal/models/reporting.py index 94405a9c52..8a666f6c96 100644 --- a/portal/models/reporting.py +++ b/portal/models/reporting.py @@ -9,6 +9,7 @@ from ..audit import auditable_event from ..cache import cache +from ..database import db from ..date_tools import FHIR_datetime from ..trigger_states.models import TriggerStatesReporting from .app_text import MailResource, SiteSummaryEmail_ATMA, app_text @@ -403,6 +404,7 @@ def generate_and_send_summaries(org_id): body=summary_email.body) try: em.send_message() + db.session.add(em) except SMTPRecipientsRefused as exc: msg = ("Error sending site summary email to {}: " "{}".format(staff_user.email, exc)) @@ -418,4 +420,5 @@ def generate_and_send_summaries(org_id): for email in exc[0]: error_emails.add(email) + db.session.commit() return error_emails or None diff --git a/portal/static/js/src/modules/TnthAjax.js b/portal/static/js/src/modules/TnthAjax.js index 92d99e334a..7e14c3949d 100644 --- a/portal/static/js/src/modules/TnthAjax.js +++ b/portal/static/js/src/modules/TnthAjax.js @@ -22,8 +22,8 @@ export default { /*global $ */ }, "sendRequest": function(url, method, userId, params, callback) { if (!url) { return false; } - var REQUEST_TIMEOUT_INTERVAL = 5000; - var defaultParams = {type: method ? method : "GET", url: url, attempts: 0, max_attempts: MAX_ATTEMPTS, contentType: "application/json; charset=utf-8", dataType: "json", sync: false, timeout: 5000, data: null, useWorker: false, async: true}; + var REQUEST_TIMEOUT_INTERVAL = 5000; // default timed out at 5 seconds + var defaultParams = {type: method ? method : "GET", url: url, attempts: 0, max_attempts: MAX_ATTEMPTS, contentType: "application/json; charset=utf-8", dataType: "json", sync: false, timeout: REQUEST_TIMEOUT_INTERVAL, data: null, useWorker: false, async: true}; params = params || defaultParams; params = $.extend({}, defaultParams, params); params.timeout = params.timeout || REQUEST_TIMEOUT_INTERVAL; @@ -75,8 +75,8 @@ export default { /*global $ */ }).fail(function(xhr) { if (params.attempts < params.max_attempts) { (function(self, url, method, userId, params, callback) { - setTimeout(function() { - self.sendRequest(url, method, userId, params, callback); + setTimeout(function () { + self.sendRequest(url, method, userId, params, callback); }, REQUEST_TIMEOUT_INTERVAL); //retry after 5 seconds })(self, url, method, userId, params, callback); } else { diff --git a/portal/static/js/src/profile.js b/portal/static/js/src/profile.js index 4f3f3246f6..bf230d61d8 100644 --- a/portal/static/js/src/profile.js +++ b/portal/static/js/src/profile.js @@ -2994,7 +2994,8 @@ export default (function() { var self = this, errorMessage = ""; $("#profileAuditLogTable").html(Utility.getLoaderHTML()); $("#profileAuditLogTable .loading-message-indicator").show(); - this.modules.tnthAjax.auditLog(this.subjectId, {useWorker:true}, function(data) { + // set request times out at 5 minutes + this.modules.tnthAjax.auditLog(this.subjectId, {useWorker:true, timeout: 5 * 60 * 1000}, function(data) { if (data.error) { errorMessage = i18next.t("Error retrieving data from server"); } diff --git a/portal/trigger_states/empro_states.py b/portal/trigger_states/empro_states.py index d0752ad9d7..6cd910966f 100644 --- a/portal/trigger_states/empro_states.py +++ b/portal/trigger_states/empro_states.py @@ -24,10 +24,10 @@ from ..models.questionnaire_bank import QuestionnaireBank from ..models.questionnaire_response import QuestionnaireResponse from ..models.observation import Observation +from ..models.research_study import EMPRO_RS_ID from ..models.user import User from ..timeout_lock import TimeoutLock -EMPRO_STUDY_ID = 1 EMPRO_LOCK_KEY = "empro-trigger-state-lock-{user_id}" @@ -317,7 +317,7 @@ def process_pending_actions(ts): # Withdrawn users should never receive reminders, nor staff # about them. qb_status = QB_Status( - user=patient, research_study_id=EMPRO_STUDY_ID, as_of_date=now) + user=patient, research_study_id=EMPRO_RS_ID, as_of_date=now) if qb_status.withdrawn_by(now): triggers = copy.deepcopy(ts.triggers) triggers['action_state'] = 'withdrawn' diff --git a/portal/views/user.py b/portal/views/user.py index 87837b4f3d..db2fb6b28d 100644 --- a/portal/views/user.py +++ b/portal/views/user.py @@ -37,6 +37,7 @@ from ..models.qb_timeline import QB_StatusCacheKey, invalidate_users_QBT from ..models.questionnaire_response import QuestionnaireResponse from ..models.relationship import Relationship +from ..models.research_study import EMPRO_RS_ID from ..models.role import ROLE, Role from ..models.table_preference import TablePreference from ..models.url_token import url_token @@ -962,8 +963,6 @@ def delete_user_consents(user_id): - ServiceToken: [] """ - from portal.trigger_states.empro_states import EMPRO_STUDY_ID - current_app.logger.debug('delete user consent called w/: {}'.format( request.json)) user = get_user(user_id, 'edit') @@ -983,7 +982,7 @@ def delete_user_consents(user_id): abort(404, "matching user consent not found") audit_comment = 'Deleted consent agreement' - if research_study_id == EMPRO_STUDY_ID: + if research_study_id == EMPRO_RS_ID: audit_comment = 'Deleted EMPRO consent agreement' remove_uc.deleted = Audit( user_id=current_user().id, subject_id=user_id, diff --git a/tests/test_assessment_status.py b/tests/test_assessment_status.py index c1bccca82d..a81a4ac269 100644 --- a/tests/test_assessment_status.py +++ b/tests/test_assessment_status.py @@ -406,7 +406,7 @@ def test_qnr_id_missing(self): def test_enrolled_in_metastatic(self): """metastatic should include baseline and indefinite""" - self.bless_with_basics(local_metastatic='metastatic') + self.bless_with_basics(local_metastatic='metastatic', setdate=now) user = db.session.merge(self.test_user) a_s = QB_Status( diff --git a/tests/test_qb_timeline.py b/tests/test_qb_timeline.py index 7d9af242b4..dde44e32d6 100644 --- a/tests/test_qb_timeline.py +++ b/tests/test_qb_timeline.py @@ -559,6 +559,34 @@ def test_indef_change_before_start_rp_w_result(self): with pytest.raises(StopIteration): next(gen) + def test_indef_assignment_post_consent(self): + org = self.setup_org_qbs(rp_name='v3', include_indef=True) + org_id = org.id + back7, nowish = associative_backdate( + now=now, backdate=relativedelta(months=7)) + self.consent_with_org(org_id=org_id, setdate=back7) + user = db.session.merge(self.test_user) + + a_s = QB_Status( + user=user, + research_study_id=0, + as_of_date=nowish) + assert a_s.enrolled_in_classification('indefinite') + + def test_indef_assignment_pre_consent(self): + org = self.setup_org_qbs(rp_name='v3', include_indef=True) + org_id = org.id + tomorrow, nowish = associative_backdate( + now=now, backdate=relativedelta(days=-1)) + self.consent_with_org(org_id=org_id, setdate=tomorrow) + user = db.session.merge(self.test_user) + + a_s = QB_Status( + user=user, + research_study_id=0, + as_of_date=nowish) + assert a_s.enrolled_in_classification('indefinite') is None + class Test_QB_StatusCacheKey(TestCase):