Skip to content

Commit

Permalink
Merge branch 'release/v17.11.22.1'
Browse files Browse the repository at this point in the history
ivan-c committed Dec 7, 2017
2 parents 2d7d46d + b4d02d8 commit eaa9194
Showing 12 changed files with 220 additions and 132 deletions.
8 changes: 5 additions & 3 deletions portal/models/assessment_status.py
Original file line number Diff line number Diff line change
@@ -11,11 +11,12 @@
from .user import User


def recent_qnr_status(user, questionnaire_name):
def recent_qnr_status(user, questionnaire_name, qb):
"""Look up recent status/timestamp for matching QuestionnaireResponse
:param user: Patient to whom completed QuestionnaireResponses belong
:param questionnaire_name: name of associated questionnaire
:param qb: QuestionnaireBank of associated questionnaire
:return: dictionary with authored (timestamp) of the most recent
QuestionnaireResponse keyed by status found
@@ -26,7 +27,8 @@ def recent_qnr_status(user, questionnaire_name):
).filter(
QuestionnaireResponse.document[
('questionnaire', 'reference')
].astext.endswith(questionnaire_name)
].astext.endswith(questionnaire_name),
QuestionnaireResponse.questionnaire_bank_id == qb.id
).order_by(
QuestionnaireResponse.status,
QuestionnaireResponse.authored).limit(9).with_entities(
@@ -96,7 +98,7 @@ def qb_status_dict(user, questionnaire_bank, as_of_date):
expired = questionnaire_bank.calculated_expiry(
trigger_date, as_of_date=as_of_date)
for q in questionnaire_bank.questionnaires:
recents = recent_qnr_status(user, q.name)
recents = recent_qnr_status(user, q.name, questionnaire_bank)
d[q.name] = status_from_recents(
recents, start, overdue, expired, as_of_date=as_of_date)
trace("QuestionnaireBank status for {}:".format(questionnaire_bank.name))
12 changes: 1 addition & 11 deletions portal/models/communication.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@
from sqlalchemy.dialects.postgresql import ENUM
from string import Formatter

from .assessment_status import AssessmentStatus # avoid cycle
from .app_text import MailResource
from ..audit import auditable_event
from ..database import db
@@ -38,16 +37,7 @@ def load_template_args(user, questionnaire_bank_id=None):
"""

def ae_link():
now = datetime.utcnow()
assessment_status = AssessmentStatus(user=user, as_of_date=now)
link_url = url_for(
'assessment_engine_api.present_assessment',
instrument_id=assessment_status.
instruments_needing_full_assessment(classification='all'),
resume_instrument_id=assessment_status.
instruments_in_progress(classification='all'),
_external=True)
return link_url
return url_for('assessment_engine_api.present_needed', _external=True)

def make_button(text):
inline = False
24 changes: 24 additions & 0 deletions portal/models/fhir.py
Original file line number Diff line number Diff line change
@@ -572,6 +572,30 @@ def aggregate_responses(instrument_ids, current_user):

return bundle


def qnr_document_id(
subject_id, questionnaire_bank_id, questionnaire_name, status):
"""Return document['identifier'] for matching QuestionnaireResponse
Using the given filter data to look for a matching QuestionnaireResponse.
Expecting to find exactly one, or raises NoResultFound
:return: the document identifier value, typically a string
"""
qnr = QuestionnaireResponse.query.filter(
QuestionnaireResponse.status == status).filter(
QuestionnaireResponse.subject_id == subject_id).filter(
QuestionnaireResponse.document[
('questionnaire', 'reference')
].astext.endswith(questionnaire_name)).filter(
QuestionnaireResponse.questionnaire_bank_id ==
questionnaire_bank_id).with_entities(
QuestionnaireResponse.document[(
'identifier', 'value')]).one()
return qnr[0]


def generate_qnr_csv(qnr_bundle):
"""Generate a CSV from a bundle of QuestionnaireResponses"""

14 changes: 2 additions & 12 deletions portal/models/intervention_strategies.py
Original file line number Diff line number Diff line change
@@ -369,13 +369,7 @@ def completed_card_html(assessment_status):
link_label = 'Continue questionnaire' if (
assessment_status.overall_status == 'In Progress') else (
'Go to questionnaire')
link_url = url_for(
'assessment_engine_api.present_assessment',
instrument_id=assessment_status.
instruments_needing_full_assessment(classification='all'),
resume_instrument_id=assessment_status.
instruments_in_progress(classification='all'))

link_url = url_for('assessment_engine_api.present_needed')
header = _(u"Open Questionnaire")
card_html = u"""
{intro}
@@ -399,11 +393,7 @@ def completed_card_html(assessment_status):
link_label = _(u'Continue questionnaire') if (
indefinite_questionnaires[1]) else (
_(u'Go to questionnaire'))
link_url = url_for(
'assessment_engine_api.present_assessment',
instrument_id=indefinite_questionnaires[0],
resume_instrument_id=indefinite_questionnaires[1])

link_url = url_for('assessment_engine_api.present_needed')
header = _(u"Open Questionnaire")
card_html = u"""
{intro}
35 changes: 7 additions & 28 deletions portal/templates/profile_macros.html
Original file line number Diff line number Diff line change
@@ -1849,11 +1849,7 @@ <h4 class="modal-title">{{ _("Enter questionnaire manually on patient's behalf")
};
$(document).ready(function() {

var continueToAssessment = function(method, completionDate, linkUrl) {
/*
* note the linkUrl is present will override the existing asssessment link
*/
var assessment_url = linkUrl || {%if person.assessment_link%}"{{person.assessment_link|safe}}"{%else%}""{%endif%};
var continueToAssessment = function(method, completionDate, assessment_url) {
if (hasValue(assessment_url)) {
var still_needed = false;

@@ -1886,7 +1882,7 @@ <h4 class="modal-title">{{ _("Enter questionnaire manually on patient's behalf")
}, 2000);

setTimeout(function() { window.location = winLocation;}, 100);

} else {
$("#manualEntryMessageContainer").html("{%trans%}The user does not have a valid assessment link.{%endtrans%}");
};
@@ -2050,9 +2046,11 @@ <h4 class="modal-title">{{ _("Enter questionnaire manually on patient's behalf")

var method = $("input[name='entryMethod']:checked").val();
var completionDate = $("#qCompletionDate").val();
var linkUrl = "{{url_for('assessment_engine_api.present_needed', subject_id=person.id)}}";

if (method != "") {
$("#manualEntryMessageContainer").text("");

if (method === "paper") {

/*
@@ -2081,33 +2079,13 @@ <h4 class="modal-title">{{ _("Enter questionnaire manually on patient's behalf")
};

if (!hasValue(errorMsg)) {

/*
* retrieve instrument ids from present questionnaire bank
* only if the date is backdated, i.e. before today's date
*/
var d = $("#qCompletionDay").val();
var m = $("#qCompletionMonth").val();
var y = $("#qCompletionYear").val();
var todayObj = tnthDates.getTodayDateObj();
var td = todayObj.displayDay, tm = todayObj.displayMonth, ty = todayObj.displayYear;

var instruments_string = "";
var linkUrl = "";

if (td+tm+ty != (pad(d)+pad(m)+pad(y))) {
/*
* note the API will redirect to AE with the necessary instruments information
*/
linkUrl = "{{url_for('assessment_engine_api.present_needed')}}" + "?subject_id={{person.id}}";
};
continueToAssessment(method, completionDate, linkUrl);
};

};
});
} else {
continueToAssessment(method);
continueToAssessment(method, completionDate, linkUrl);
};
} else {
$("#manualEntryMessageContainer").html("{%trans%}You must select a method.{%endtrans%}");
@@ -2118,7 +2096,8 @@ <h4 class="modal-title">{{ _("Enter questionnaire manually on patient's behalf")

tnthAjax.assessmentStatus({{person.id}}, function(data) {
if (!data.error) {
if ((data.assessment_status).toUpperCase() == "COMPLETED") {
if (((data.assessment_status).toUpperCase() == "COMPLETED") &&
(parseInt(data.outstanding_indefinite_work) === 0)) {
$("#assessmentLink").attr("disabled", true);
$("#enterManualInfoContainer").text("{%trans%}All available questionnaires have been completed.{%endtrans%}");
};
4 changes: 3 additions & 1 deletion portal/templates/settings.html
Original file line number Diff line number Diff line change
@@ -12,7 +12,9 @@ <h3 class="tnth-headline">{{ _("System Settings and Status") }}</h3>
{{ form.hidden_tag() }}

{{ render_field(form.timeout, tabindex=5) }}
{{ render_field(form.import_orgs, tabindex=7) }}
{{ render_field(form.patient_id, tabindex=6) }}
{{ render_field(form.timestamp, tabindex=7) }}
{{ render_field(form.import_orgs, tabindex=8) }}

{{ trace_data }}
<button type="submit" class="btn btn-tnth-primary">Submit</button>
51 changes: 37 additions & 14 deletions portal/views/assessment_engine.py
Original file line number Diff line number Diff line change
@@ -16,7 +16,12 @@
from ..models.assessment_status import invalidate_assessment_status_cache
from ..models.assessment_status import overall_assessment_status
from ..models.auth import validate_origin
from ..models.fhir import QuestionnaireResponse, EC, aggregate_responses, generate_qnr_csv
from ..models.fhir import (
aggregate_responses,
EC,
generate_qnr_csv,
QuestionnaireResponse,
qnr_document_id)
from ..models.intervention import INTERVENTION
from ..models.questionnaire import Questionnaire
from ..models.questionnaire_bank import QuestionnaireBank
@@ -1355,24 +1360,30 @@ def present_needed():
if subject != current_user():
current_user().check_role(permission='edit', other_id=subject_id)

as_of_date = FHIR_datetime.parse(request.args.get('authored'))
authored = request.args.get('authored')
as_of_date = FHIR_datetime.parse(authored) if authored else None
assessment_status = AssessmentStatus(subject, as_of_date=as_of_date)
args = dict(request.args.items())
args['instrument_id'] = (
assessment_status.instruments_needing_full_assessment(
classification='all'))

# As the AssessmentEngine isn't yet equipped to restart out
# of sequence instruments, treat all as new if as_of_date
# isn't today.
if as_of_date and as_of_date.date() != datetime.utcnow().date():
args['instrument_id'] += (
assessment_status.instruments_in_progress(
classification='all'))
else:
args['resume_instrument_id'] = (
assessment_status.instruments_in_progress(
classification='all'))
# If we find any instruments_in_progress, need to fetch their
# identifiers for reliable resume behavior on the AE side.
# This is also done now to avoid the overhead of looking up
# when generating reports and reminders.
resume_ids = []
for questionnaire_name in assessment_status.instruments_in_progress(
classification='all'):
resume_ids.append(
qnr_document_id(
subject_id=subject_id,
questionnaire_bank_id=assessment_status.qb_data.qb.id,
questionnaire_name=questionnaire_name,
status='in-progress'))

if resume_ids:
args['resume_identifier'] = resume_ids

url = url_for('.present_assessment', **args)
return redirect(url, code=303)
@@ -1453,6 +1464,7 @@ def present_assessment(instruments=None):

queued_instruments = request.args.getlist('instrument_id')
resume_instruments = request.args.getlist('resume_instrument_id')
resume_identifiers = request.args.getlist('resume_identifier')

# Hack to allow deprecated API to piggyback
# Remove when deprecated_present_assessment() is fully removed
@@ -1477,6 +1489,7 @@ def present_assessment(instruments=None):
assessment_params = {
"project": ",".join(common_instruments),
"resume_instrument_id": ",".join(resume_instruments),
"resume_identifier": ",".join(resume_identifiers),
"subject_id": request.args.get('subject_id'),
"authored": request.args.get('authored'),
}
@@ -1680,7 +1693,17 @@ def patient_assessment_status(patient_id):
assessment_overall_status = (
assessment_status.overall_status if assessment_status else
None)
return jsonify(assessment_status=assessment_overall_status)

# indefinite assessments don't affect overall status, but need to
# be available if unfinished
outstanding_indefinite_work = len(
assessment_status.instruments_needing_full_assessment(
classification='indefinite') +
assessment_status.instruments_in_progress(
classification='indefinite'))

return jsonify(assessment_status=assessment_overall_status,
outstanding_indefinite_work=outstanding_indefinite_work)
else:
abort(400, "invalid patient id")

4 changes: 0 additions & 4 deletions portal/views/patients.py
Original file line number Diff line number Diff line change
@@ -168,10 +168,6 @@ def patient_profile(patient_id):
if (display.access and display.link_url is not None and
display.link_label is not None):
user_interventions.append({"name": intervention.name})
if intervention.name == 'assessment_engine':
# Need to extend with subject_id as the staff user is driving
patient.assessment_link = '{url}&subject_id={id}'.format(
url=display.link_url, id=patient.id)

return render_template(
'profile.html', user=patient,
28 changes: 26 additions & 2 deletions portal/views/portal.py
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
from ..audit import auditable_event
from .crossdomain import crossdomain
from ..database import db
from ..date_tools import FHIR_datetime
from ..factories.celery import create_celery
from ..extensions import oauth, user_manager
from ..models.app_text import (
@@ -30,9 +31,11 @@
UserInviteEmail_ATMA,
VersionedResource
)
from ..models.assessment_status import invalidate_assessment_status_cache
from ..models.auth import validate_origin
from ..models.communication import load_template_args, Communication
from ..models.coredata import Coredata
from ..models.fhir import QuestionnaireResponse
from ..models.i18n import get_locale
from ..models.identifier import Identifier
from ..models.message import EmailMessage
@@ -42,7 +45,7 @@
from ..models.table_preference import TablePreference
from ..models.user import current_user, get_user, User
from ..system_uri import SHORTCUT_ALIAS
from ..trace import establish_trace, dump_trace
from ..trace import establish_trace, dump_trace, trace


portal = Blueprint('portal', __name__)
@@ -572,6 +575,8 @@ def contact_sent(message_id):
class SettingsForm(FlaskForm):
timeout = IntegerField('Session Timeout for This Web Browser (in seconds)',
validators=[validators.DataRequired()])
patient_id = IntegerField('Patient to edit', validators=[validators.optional()])
timestamp = StringField("Datetime string for patient's questionnaire_responses, format YYYY-MM-DD")
import_orgs = BooleanField('Import Organizations from Site Persistence')


@@ -605,7 +610,6 @@ def settings():
wide_container="true")

if form.import_orgs.data:
from ..trace import dump_trace, establish_trace, trace
from ..config.model_persistence import ModelPersistence
establish_trace("Initiate import...")
try:
@@ -620,6 +624,26 @@ def settings():
OrgTree().invalidate_cache()
organization_consents = Organization.consent_agreements()

if form.patient_id.data and form.timestamp.data:
patient = get_user(form.patient_id.data)
if not patient:
trace("Patient Not found {}".format(form.patient_id.data))
try:
dt = FHIR_datetime.parse(form.timestamp.data)
for qnr in QuestionnaireResponse.query.filter_by(subject_id=patient.id):
qnr.authored = dt
document = qnr.document
document['authored'] = FHIR_datetime.as_fhir(dt)
# Due to the infancy of JSON support in POSTGRES and SQLAlchemy
# one must force the update to get a JSON field change to stick
db.session.query(QuestionnaireResponse).filter(
QuestionnaireResponse.id == qnr.id).update({"document": document})
db.session.commit()
invalidate_assessment_status_cache(patient.id)
except ValueError as e:
trace("Invalid date format {}".format(form.timestamp.data))
trace("ERROR: {}".format(e))

# make max_age outlast the browser session
max_age = 60 * 60 * 24 * 365 * 5
response = make_response(render_template(
135 changes: 98 additions & 37 deletions tests/test_assessment_status.py
Original file line number Diff line number Diff line change
@@ -2,13 +2,16 @@
from datetime import datetime
from dateutil.relativedelta import relativedelta
from flask_webtest import SessionScope
from random import choice
from sqlalchemy.orm.exc import NoResultFound
from string import ascii_letters

from portal.extensions import db
from portal.models.assessment_status import invalidate_assessment_status_cache
from portal.models.assessment_status import AssessmentStatus
from portal.models.audit import Audit
from portal.models.encounter import Encounter
from portal.models.fhir import CC, QuestionnaireResponse
from portal.models.fhir import CC, QuestionnaireResponse, qnr_document_id
from portal.models.intervention import INTERVENTION
from portal.models.organization import Organization
from portal.models.questionnaire import Questionnaire
@@ -17,35 +20,48 @@
from portal.models.recur import Recur
from portal.models.research_protocol import ResearchProtocol
from portal.models.role import ROLE
from portal.models.user import get_user
from tests import TestCase, TEST_USER_ID


def mock_qr(user_id, instrument_id, status='completed', timestamp=None):
def mock_qr(
instrument_id, status='completed', timestamp=None, qb=None,
doc_id=None):
if not doc_id:
doc_id = ''.join(choice(ascii_letters) for _ in range(10))
timestamp = timestamp or datetime.utcnow()
qr_document = {
"questionnaire": {
"display": "Additional questions",
"reference":
"https://{}/api/questionnaires/{}".format(
'SERVER_NAME', instrument_id)
}
'SERVER_NAME', instrument_id)},
"identifier": {
"use": "official",
"label": "cPRO survey session ID",
"value": doc_id,
"system": "https://stg-ae.us.truenth.org/eproms-demo"}
}

enc = Encounter(status='planned', auth_method='url_authenticated',
user_id=TEST_USER_ID, start_time=timestamp)
with SessionScope(db):
db.session.add(enc)
db.session.commit()
enc = db.session.merge(enc)
qb = qb or QuestionnaireBank.most_current_qb(get_user(TEST_USER_ID),
timestamp).questionnaire_bank
qr = QuestionnaireResponse(
subject_id=TEST_USER_ID,
status=status,
authored=timestamp,
document=qr_document,
encounter_id=enc.id)
encounter_id=enc.id,
questionnaire_bank=qb)
with SessionScope(db):
db.session.add(qr)
db.session.commit()
invalidate_assessment_status_cache(user_id)
invalidate_assessment_status_cache(TEST_USER_ID)


localized_instruments = set(['eproms_add', 'epic26', 'comorb'])
@@ -297,6 +313,25 @@ def mark_metastatic(self):


class TestAssessmentStatus(TestQuestionnaireSetup):

def test_qnr_id(self):
qb = QuestionnaireBank.query.first()
mock_qr(
instrument_id='irondemog',
status='in-progress', qb=qb,
doc_id='two11')
qb = db.session.merge(qb)
result = qnr_document_id(
TEST_USER_ID, qb.id, 'irondemog', 'in-progress')
self.assertEquals(result, 'two11')

def test_qnr_id_missing(self):
qb = QuestionnaireBank.query.first()
qb = db.session.merge(qb)
with self.assertRaises(NoResultFound):
result = qnr_document_id(
TEST_USER_ID, qb.id, 'irondemog', 'in-progress')

def test_enrolled_in_metastatic(self):
"""metastatic should include baseline and indefinite"""
self.bless_with_basics()
@@ -332,9 +367,9 @@ def test_localized_on_time(self):
# User finished both on time
self.bless_with_basics() # pick up a consent, etc.
self.mark_localized()
mock_qr(user_id=TEST_USER_ID, instrument_id='eproms_add')
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26')
mock_qr(user_id=TEST_USER_ID, instrument_id='comorb')
mock_qr(instrument_id='eproms_add')
mock_qr(instrument_id='epic26')
mock_qr(instrument_id='comorb')

self.test_user = db.session.merge(self.test_user)
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
@@ -348,12 +383,9 @@ def test_localized_inprogress_on_time(self):
# User finished both on time
self.bless_with_basics() # pick up a consent, etc.
self.mark_localized()
mock_qr(user_id=TEST_USER_ID, instrument_id='eproms_add',
status='in-progress')
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26',
status='in-progress')
mock_qr(user_id=TEST_USER_ID, instrument_id='comorb',
status='in-progress')
mock_qr(instrument_id='eproms_add', status='in-progress')
mock_qr(instrument_id='epic26', status='in-progress')
mock_qr(instrument_id='comorb', status='in-progress')

self.test_user = db.session.merge(self.test_user)
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
@@ -368,7 +400,7 @@ def test_localized_in_process(self):
# User finished one, time remains for other
self.bless_with_basics() # pick up a consent, etc.
self.mark_localized()
mock_qr(user_id=TEST_USER_ID, instrument_id='eproms_add')
mock_qr(instrument_id='eproms_add')

self.test_user = db.session.merge(self.test_user)
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
@@ -384,11 +416,13 @@ def test_localized_in_process(self):
def test_metastatic_on_time(self):
# User finished both on time
self.bless_with_basics() # pick up a consent, etc.
self.mark_metastatic()
for i in metastatic_baseline_instruments:
mock_qr(user_id=TEST_USER_ID, instrument_id=i)
mock_qr(user_id=TEST_USER_ID, instrument_id='irondemog')
mock_qr(instrument_id=i)
mi_qb = QuestionnaireBank.query.filter_by(
name='metastatic_indefinite').first()
mock_qr(instrument_id='irondemog', qb=mi_qb)

self.mark_metastatic()
self.test_user = db.session.merge(self.test_user)
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
self.assertEquals(a_s.overall_status, "Completed")
@@ -421,11 +455,11 @@ def test_localized_overdue(self):
# if the user completed something on time, and nothing else
# is due, should see the thankyou message.

# backdate so the baseline q's have expired
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26',
status='in-progress')
self.bless_with_basics(backdate=relativedelta(months=3))
self.mark_localized()
# backdate so the baseline q's have expired
mock_qr(instrument_id='epic26', status='in-progress')

self.test_user = db.session.merge(self.test_user)
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
self.assertEquals(a_s.overall_status, "Partially Completed")
@@ -440,12 +474,12 @@ def test_localized_as_of_date(self):
# backdating consent beyond expired and the status lookup date
# within a valid window should show available assessments.

# backdate so the baseline q's have expired
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26',
status='in-progress',
timestamp=datetime.utcnow() - relativedelta(months=3))
self.bless_with_basics(backdate=relativedelta(months=3))
self.mark_localized()
# backdate so the baseline q's have expired
mock_qr(instrument_id='epic26', status='in-progress',
timestamp=datetime.utcnow() - relativedelta(months=3))

self.test_user = db.session.merge(self.test_user)
as_of_date = datetime.utcnow() - relativedelta(months=2, days=28)
a_s = AssessmentStatus(user=self.test_user, as_of_date=as_of_date)
@@ -462,12 +496,12 @@ def test_metastatic_as_of_date(self):
# backdating consent beyond expired and the status lookup date
# within a valid window should show available assessments.

# backdate so the baseline q's have expired
mock_qr(user_id=TEST_USER_ID, instrument_id='epic23',
status='in-progress',
timestamp=datetime.utcnow() - relativedelta(months=3))
self.bless_with_basics(backdate=relativedelta(months=3))
self.mark_metastatic()
# backdate so the baseline q's have expired
mock_qr(instrument_id='epic23', status='in-progress',
timestamp=datetime.utcnow() - relativedelta(months=3))

self.test_user = db.session.merge(self.test_user)
as_of_date = datetime.utcnow() - relativedelta(months=2, days=28)
a_s = AssessmentStatus(user=self.test_user, as_of_date=as_of_date)
@@ -494,10 +528,41 @@ def test_initial_recur_due(self):
set(a_s.instruments_needing_full_assessment()),
metastatic_3)

def test_initial_recur_baseline_done(self):
# backdate to be within the first recurrence window

self.bless_with_basics(backdate=relativedelta(months=3, days=2))
self.mark_metastatic()

# add baseline QNRs, as if submitted nearly 3 months ago, during
# baseline window
backdated = datetime.utcnow() - relativedelta(months=2, days=25)
baseline = QuestionnaireBank.query.filter_by(
name='metastatic').one()
for instrument in metastatic_baseline_instruments:
mock_qr(instrument, qb=baseline, timestamp=backdated)

self.test_user = db.session.merge(self.test_user)
# Check status during baseline window
a_s_baseline = AssessmentStatus(
user=self.test_user, as_of_date=backdated)
self.assertEquals(a_s_baseline.overall_status, "Completed")
self.assertFalse(a_s_baseline.instruments_needing_full_assessment())

# Whereas "current" status for the initial recurrence show due.
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
self.assertEquals(a_s.overall_status, "Due")

# in the initial window w/ no questionnaires submitted
# should include all from initial recur
self.assertEquals(
set(a_s.instruments_needing_full_assessment()),
metastatic_3)

def test_secondary_recur_due(self):

# backdate so baseline q's have expired, and we are within the
# second recurrance window
# second recurrence window
self.bless_with_basics(backdate=relativedelta(months=6))
self.mark_metastatic()
self.test_user = db.session.merge(self.test_user)
@@ -553,9 +618,7 @@ def test_boundry_in_progress(self):
self.bless_with_basics(backdate=relativedelta(months=3, hours=-1))
self.mark_localized()
for instrument in localized_instruments:
mock_qr(
user_id=TEST_USER_ID, instrument_id=instrument,
status='in-progress')
mock_qr(instrument_id=instrument, status='in-progress')
self.test_user = db.session.merge(self.test_user)
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
self.assertEquals(a_s.overall_status, 'In Progress')
@@ -565,9 +628,7 @@ def test_boundry_in_progress_expired(self):
self.bless_with_basics(backdate=relativedelta(months=3))
self.mark_localized()
for instrument in localized_instruments:
mock_qr(
user_id=TEST_USER_ID, instrument_id=instrument,
status='in-progress')
mock_qr(instrument_id=instrument, status='in-progress')
self.test_user = db.session.merge(self.test_user)
a_s = AssessmentStatus(user=self.test_user, as_of_date=None)
self.assertEquals(a_s.overall_status, 'Partially Completed')
30 changes: 12 additions & 18 deletions tests/test_communication.py
Original file line number Diff line number Diff line change
@@ -161,12 +161,9 @@ def test_nearready_message(self):
self.bless_with_basics(backdate=timedelta(days=13))
self.promote_user(role_name=ROLE.PATIENT)
self.mark_localized()
mock_qr(user_id=TEST_USER_ID, instrument_id='eproms_add',
status='in-progress')
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26',
status='in-progress')
mock_qr(user_id=TEST_USER_ID, instrument_id='comorb',
status='in-progress')
mock_qr(instrument_id='eproms_add', status='in-progress')
mock_qr(instrument_id='epic26', status='in-progress')
mock_qr(instrument_id='comorb', status='in-progress')

update_patient_loop(update_cache=False, queue_messages=True)
expected = Communication.query.first()
@@ -184,12 +181,9 @@ def test_ready_message(self):
self.bless_with_basics(backdate=timedelta(days=14))
self.promote_user(role_name=ROLE.PATIENT)
self.mark_localized()
mock_qr(user_id=TEST_USER_ID, instrument_id='eproms_add',
status='in-progress')
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26',
status='in-progress')
mock_qr(user_id=TEST_USER_ID, instrument_id='comorb',
status='in-progress')
mock_qr(instrument_id='eproms_add', status='in-progress')
mock_qr(instrument_id='epic26', status='in-progress')
mock_qr(instrument_id='comorb', status='in-progress')

update_patient_loop(update_cache=False, queue_messages=True)
expected = Communication.query.first()
@@ -259,9 +253,9 @@ def test_done_message(self):
self.bless_with_basics(backdate=timedelta(days=14))
self.promote_user(role_name=ROLE.PATIENT)
self.mark_localized()
mock_qr(user_id=TEST_USER_ID, instrument_id='eproms_add')
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26')
mock_qr(user_id=TEST_USER_ID, instrument_id='comorb')
mock_qr(instrument_id='eproms_add')
mock_qr(instrument_id='epic26')
mock_qr(instrument_id='comorb')

update_patient_loop(update_cache=False, queue_messages=True)
expected = Communication.query.first()
@@ -350,7 +344,7 @@ def test_st_done(self):
QuestionnaireBank.qbs_for_user(self.test_user, 'baseline'))

for instrument in symptom_tracker_instruments:
mock_qr(user_id=TEST_USER_ID, instrument_id=instrument)
mock_qr(instrument_id=instrument)

# With all q's done, shouldn't generate a message
update_patient_loop(update_cache=False, queue_messages=True)
@@ -371,7 +365,7 @@ def test_st_undone(self):
QuestionnaireBank.qbs_for_user(self.test_user, 'baseline'))

# With most q's undone, should generate a message
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26')
mock_qr(instrument_id='epic26')
a_s, _ = overall_assessment_status(TEST_USER_ID)
self.assertEquals('In Progress', a_s)
update_patient_loop(update_cache=False, queue_messages=True)
@@ -395,7 +389,7 @@ def test_st_metastatic(self):
QuestionnaireBank.qbs_for_user(self.test_user, 'baseline'))

# shouldn't generate a message either
mock_qr(user_id=TEST_USER_ID, instrument_id='epic26')
mock_qr(instrument_id='epic26')
update_patient_loop(update_cache=False, queue_messages=True)
expected = Communication.query.first()
self.assertFalse(expected)
7 changes: 5 additions & 2 deletions tests/test_intervention.py
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
from portal.models.intervention_strategies import AccessStrategy
from portal.models.message import EmailMessage
from portal.models.organization import Organization
from portal.models.questionnaire_bank import QuestionnaireBank
from portal.models.role import ROLE
from portal.models.user import add_role
from portal.system_uri import DECISION_SUPPORT_GROUP, SNOMED
@@ -402,8 +403,10 @@ def test_card_html_update(self):
dt = datetime(2017, 6, 10, 20, 00, 00, 000000)
# Add a fake assessments and see a change
for i in metastatic_baseline_instruments:
mock_qr(user_id=TEST_USER_ID, instrument_id=i, timestamp=dt)
mock_qr(user_id=TEST_USER_ID, instrument_id='irondemog', timestamp=dt)
mock_qr(instrument_id=i, timestamp=dt)
mi_qb = QuestionnaireBank.query.filter_by(
name='metastatic_indefinite').first()
mock_qr(instrument_id='irondemog', timestamp=dt, qb=mi_qb)

user, ae = map(db.session.merge, (self.test_user, ae))

0 comments on commit eaa9194

Please sign in to comment.