Skip to content

Commit cf5f46c

Browse files
committed
Merge remote-tracking branch 'biocore/master' into master-overhaul
2 parents 2117337 + 07a5757 commit cf5f46c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+3610
-1251
lines changed

ci/pip_requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
openapi-spec-validator < 0.2.10
2+
swagger-ui-bundle==0.0.9
23
connexion[swagger-ui] < 2.7.1
34
jsonschema < 4.0.0
45
pyjwt[crypto] < 2.2.0

microsetta_private_api/LEGACY/locale_data/japanese_gut.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,24 @@
66
_NEW_PARTICIPANT = {
77
'ADD_HUMAN_TITLE': '新しいプロフィール情報を追加する',
88
'SEL_AGE_RANGE': '参加者の年齢層を選択する:',
9-
'AGE_0_6': '',
10-
'AGE_7_12': '',
11-
'AGE_13_17': '',
9+
'AGE_0_6': '3ヶ月 - 6才',
10+
'AGE_7_12': '7〜12才',
11+
'AGE_13_17': '13〜17年',
1212
'AGE_18': '18歳以上',
1313

1414
'PARTICIPATION_AGREEMENT': '''''',
1515

1616
'EXHIBIT_A': '''''',
1717
'BILL_OF_RIGHTS': '''研究被験者の権利章典''',
1818
'TEXT_I_HAVE_READ_1': '私は同意書を読みました (又は代読してもらいました)。研究試験への参加を求められていることを理解し、この試験への参加に自由意志で同意します。私の個人データが処理される方法、関連した私の権利を理解しており、この同意書に記載されているように私のデータが処理されることに同意します。',
19-
'TEXT_I_HAVE_READ_SIMPLIFIED': '',
20-
'PERSON_ATTAINING_ASSENT': '',
21-
'TEXT_ASSENT_WITNESS': '',
22-
'OBTAINER_NAME': '',
23-
'TEXT_I_HAVE_READ_PARENT': '',
19+
'TEXT_I_HAVE_READ_SIMPLIFIED': 'はい、あなたはこの調査研究に参加します。',
20+
'PERSON_ATTAINING_ASSENT': '同意を得る人の署名',
21+
'TEXT_ASSENT_WITNESS': '私の判断において、参加者は自発的かつ自主的に同意し、研究に参加する意思を示す法的能力を持っています。',
22+
'OBTAINER_NAME': '同意を得る人の名前',
23+
'TEXT_I_HAVE_READ_PARENT': '私はこのフォームを読みました(または誰かが代読した)。私は、我が子の調査研究への参加に、同意を求められていることを理解しました。私は自発的に我が子がこの研究に参加することに同意します。我が子の個人データの処理方法、これに関連する権利、そして提供す',
2424
'PARTICIPANT_NAME': '参加者名',
2525
'PARTICIPANT_EMAIL': 'Eメール',
26-
'PARTICIPANT_PARENT_1': '',
26+
'PARTICIPANT_PARENT_1': '親/保護者の名前',
2727
'PARTICIPANT_PARENT_2': '',
2828
'PARTICIPANT_DECEASED_PARENTS': '',
2929
'DATE_SIGNED': 'DATE_SIGNED',

microsetta_private_api/admin/admin_impl.py

+18
Original file line numberDiff line numberDiff line change
@@ -919,3 +919,21 @@ def get_vioscreen_sample_to_user(token_info):
919919
st_repo = SurveyTemplateRepo(t)
920920
data = st_repo.get_vioscreen_sample_to_user()
921921
return jsonify(data), 200
922+
923+
924+
def get_perk_fulfillment_state(token_info):
925+
validate_admin_access(token_info)
926+
with Transaction() as t:
927+
admin_repo = AdminRepo(t)
928+
pf_state = admin_repo.get_perk_fulfillment_state()
929+
return jsonify({"pf_state": pf_state}), 200
930+
931+
932+
def update_perk_fulfillment_state(token_info, perk_fulfillment_state):
933+
validate_admin_access(token_info)
934+
with Transaction() as t:
935+
admin_repo = AdminRepo(t)
936+
admin_repo.update_perk_fulfillment_state(perk_fulfillment_state)
937+
pf_state = admin_repo.get_perk_fulfillment_state()
938+
t.commit()
939+
return jsonify({"pf_state": pf_state}), 200

microsetta_private_api/admin/email_templates.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class EmailMessage(Enum):
2626
EventSubtype.EMAIL_ACTIVATION
2727
)
2828
incorrect_sample_type = (
29-
gettext("Your Microsetta Initiative status update: attention needed"),
29+
gettext("Microsetta Initiative update: attention needed"),
3030
"email/incorrect_sample_type.jinja2",
3131
("contact_name", "sample_barcode", "recorded_type", "received_type",
3232
"resolution_url"),
@@ -35,21 +35,21 @@ class EmailMessage(Enum):
3535
)
3636
missing_sample_info = (
3737
gettext(
38-
"Your Microsetta Initiative status update: information needed"),
38+
"Microsetta Initiative update: information needed"),
3939
"email/missing_sample_info.jinja2",
4040
("contact_name", "sample_barcode", "received_type", "resolution_url"),
4141
EventType.EMAIL,
4242
EventSubtype.EMAIL_MISSING_SAMPLE_INFO
4343
)
4444
sample_is_valid = (
45-
gettext("Your Microsetta Initiative status update and next steps"),
45+
gettext("We received your Microsetta Initiative sample"),
4646
"email/sample_is_valid.jinja2",
4747
("contact_name",),
4848
EventType.EMAIL,
4949
EventSubtype.EMAIL_SAMPLE_IS_VALID
5050
)
5151
no_associated_source = (
52-
gettext("Your Microsetta Initiative status update: "
52+
gettext("Microsetta Initiative update: "
5353
"critical information needed"),
5454
"email/no_associated_source.jinja2",
5555
("contact_name", "sample_barcode", "resolution_url"),
@@ -65,7 +65,7 @@ class EmailMessage(Enum):
6565
)
6666
address_invalid = (
6767
gettext(
68-
"Your Microsetta Initiative status update: information needed"),
68+
"Microsetta Initiative update: information needed"),
6969
"email/address_invalid.jinja2",
7070
("contact_name", "resolution_url"),
7171
EventType.EMAIL,
@@ -81,23 +81,23 @@ class EmailMessage(Enum):
8181
)
8282
thank_you_with_kit = (
8383
gettext(
84-
"Registration code & kit update!"),
84+
"Microsetta Initiative registration code & kit update"),
8585
"email/thank_you_with_kit.jinja2",
8686
("first_name", "registration_code", "interface_endpoint"),
8787
EventType.EMAIL,
8888
EventSubtype.EMAIL_THANK_YOU_WITH_KIT
8989
)
9090
thank_you_no_kit = (
9191
gettext(
92-
"Your questionnaire is ready!"),
92+
"Your Microsetta Initiative questionnaire is ready"),
9393
"email/thank_you_no_kit.jinja2",
9494
("first_name", "registration_code", "interface_endpoint"),
9595
EventType.EMAIL,
9696
EventSubtype.EMAIL_THANK_YOU_NO_KIT
9797
)
9898
kit_tracking_number = (
9999
gettext(
100-
"Your kit is on its way!"),
100+
"Your Microsetta Initiative kit has shipped"),
101101
"email/kit_tracking_number.jinja2",
102102
("first_name", "tracking_number"),
103103
EventType.EMAIL,

microsetta_private_api/admin/tests/test_admin_repo.py

+40
Original file line numberDiff line numberDiff line change
@@ -1344,3 +1344,43 @@ def test_create_ffq_code(self):
13441344
)
13451345
row = cur.fetchone()
13461346
self.assertEqual(row['registration_code_used'], None)
1347+
1348+
def test_get_perk_fulfillment_state(self):
1349+
with Transaction() as t:
1350+
admin_repo = AdminRepo(t)
1351+
with t.cursor() as cur:
1352+
cur.execute(
1353+
"UPDATE ag.settings "
1354+
"SET perk_fulfillment_active = TRUE"
1355+
)
1356+
1357+
obs = admin_repo.get_perk_fulfillment_state()
1358+
self.assertTrue(obs)
1359+
1360+
cur.execute(
1361+
"UPDATE ag.settings "
1362+
"SET perk_fulfillment_active = False"
1363+
)
1364+
1365+
obs = admin_repo.get_perk_fulfillment_state()
1366+
self.assertFalse(obs)
1367+
1368+
def test_update_perk_fulfillment_state(self):
1369+
with Transaction() as t:
1370+
with t.cursor() as cur:
1371+
admin_repo = AdminRepo(t)
1372+
admin_repo.update_perk_fulfillment_state(True)
1373+
cur.execute(
1374+
"SELECT perk_fulfillment_active "
1375+
"FROM ag.settings"
1376+
)
1377+
obs = cur.fetchone()
1378+
self.assertTrue(obs[0])
1379+
1380+
admin_repo.update_perk_fulfillment_state(False)
1381+
cur.execute(
1382+
"SELECT perk_fulfillment_active "
1383+
"FROM ag.settings"
1384+
)
1385+
obs = cur.fetchone()
1386+
self.assertFalse(obs[0])

microsetta_private_api/api/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
from ._source import (
1919
create_source, read_source, update_source, read_sources,
2020
create_human_source_from_consent, check_duplicate_source_name,
21-
scrub_source, check_source_ffq_prereqs, check_prompt_survey_update
21+
scrub_source, check_source_ffq_prereqs, check_prompt_survey_update,
22+
get_external_reports, get_external_report, get_external_report_bytes
2223
)
2324
from ._survey import (
2425
read_survey_template, read_survey_templates, read_answered_survey,
@@ -84,6 +85,9 @@
8485
'read_source',
8586
'check_source_ffq_prereqs',
8687
'check_prompt_survey_update',
88+
'get_external_reports',
89+
'get_external_report',
90+
'get_external_report_bytes',
8791
'update_source',
8892
'scrub_source',
8993
'read_sources',

microsetta_private_api/api/_account.py

+5
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ def check_email_match(account_id, token_info):
124124
def update_account(account_id, body, token_info):
125125
acc = _validate_account_access(token_info, account_id)
126126

127+
# Prevent users outside Japan from selecting Japanese
128+
if body['language'] == "ja_JP" and\
129+
body['address']['country_code'] != "JP":
130+
return jsonify(code=422, message="Invalid language selected"), 422
131+
127132
with Transaction() as t:
128133
acct_repo = AccountRepo(t)
129134
acc.first_name = body['first_name']

microsetta_private_api/api/_consent.py

+58-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
from microsetta_private_api import localization
44
from microsetta_private_api.api._account import \
55
_validate_account_access
6-
from microsetta_private_api.model.consent import ConsentSignature
6+
from microsetta_private_api.model.consent import ConsentSignature,\
7+
HUMAN_CONSENT_AGE_GROUPS
78
from microsetta_private_api.repo.consent_repo import ConsentRepo
9+
from microsetta_private_api.repo.source_repo import SourceRepo
810
from microsetta_private_api.repo.transaction import Transaction
911
from microsetta_private_api.api.literals import CONSENT_DOC_NOT_FOUND_MSG
1012
from werkzeug.exceptions import NotFound
13+
from microsetta_private_api.api.literals import SRC_NOT_FOUND_MSG
1114

1215

1316
def render_consent_doc(account_id, language_tag, token_info):
@@ -47,7 +50,12 @@ def check_consent_signature(account_id, source_id, consent_type, token_info):
4750

4851
with Transaction() as t:
4952
consent_repo = ConsentRepo(t)
50-
res = consent_repo.is_consent_required(source_id, consent_type)
53+
source_repo = SourceRepo(t)
54+
source = source_repo.get_source(account_id, source_id)
55+
age_range = source.source_data.age_range
56+
res = consent_repo.is_consent_required(
57+
source_id, age_range, consent_type
58+
)
5159

5260
return jsonify({"result": res}), 200
5361

@@ -56,6 +64,54 @@ def sign_consent_doc(account_id, source_id, consent_type, body, token_info):
5664
_validate_account_access(token_info, account_id)
5765

5866
with Transaction() as t:
67+
# Sources are now permitted to update their age range, but only if it
68+
# moves the source to an older age group. For this purpose, "legacy"
69+
# is treated as younger than "0-6", as they're choosing an age group
70+
# for the first time.
71+
source_repo = SourceRepo(t)
72+
source = source_repo.get_source(account_id, source_id)
73+
if source is None:
74+
return jsonify(code=404, message=SRC_NOT_FOUND_MSG), 404
75+
76+
if source.source_data.age_range != body['age_range']:
77+
# Let's make sure it's a valid change. First, grab the index of
78+
# their current age range.
79+
try:
80+
cur_age_index = HUMAN_CONSENT_AGE_GROUPS.index(
81+
source.source_data.age_range
82+
)
83+
except ValueError:
84+
# Catch any sources that have a blank, "legacy", or faulty
85+
# age_range
86+
cur_age_index = -1
87+
88+
# Next, make sure their new age range is valid
89+
try:
90+
new_age_index = HUMAN_CONSENT_AGE_GROUPS.index(
91+
body['age_range']
92+
)
93+
except ValueError:
94+
# Shouldn't reach this point, but if we do, reject it
95+
return jsonify(
96+
code=403, message="Invalid age_range update"
97+
), 403
98+
99+
# Finally, make sure the new age_range isn't younger than the
100+
# current age_range.
101+
if new_age_index < cur_age_index:
102+
return jsonify(
103+
code=403, message="Invalid age_range update"
104+
), 403
105+
106+
update_success = source_repo.update_source_age_range(
107+
source_id, body['age_range']
108+
)
109+
if not update_success:
110+
return jsonify(
111+
code=403, message="Invalid age_range update"
112+
), 403
113+
114+
# Now back to the normal flow of signing a consent document
59115
consent_repo = ConsentRepo(t)
60116
sign_id = str(uuid.uuid4())
61117
consent_sign = ConsentSignature.from_dict(body, source_id, sign_id)

microsetta_private_api/api/_interested_user.py

+26-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
from microsetta_private_api.exceptions import RepoException
88
from microsetta_private_api.repo.campaign_repo import CampaignRepo
99
from microsetta_private_api.repo.melissa_repo import MelissaRepo
10+
from microsetta_private_api.repo.perk_fulfillment_repo import\
11+
PerkFulfillmentRepo
1012
from microsetta_private_api.tasks import send_email
13+
from microsetta_private_api.admin.admin_impl import validate_admin_access
1114

1215

1316
def create_interested_user(body):
@@ -105,18 +108,12 @@ def get_interested_user_address_update(interested_user_id, email):
105108
else:
106109
# We've determined it's a valid user and their address
107110
# needs to be fixed, so we return a subset of their info.
108-
# Certain paths into the database allow null address_2
109-
# which causes problems, so we change it to "".
110-
if interested_user.address_2 is None:
111-
interested_user.address_2 = ""
112-
113111
# We also need to grab the reason(s) that Melissa couldn't
114112
# verify the address.
115113
melissa_repo = MelissaRepo(t)
116114
melissa_result = melissa_repo.check_duplicate(
117115
interested_user.address_1,
118116
interested_user.address_2,
119-
interested_user.address_3,
120117
interested_user.postal_code,
121118
interested_user.country
122119
)
@@ -146,6 +143,7 @@ def get_interested_user_address_update(interested_user_id, email):
146143
state=interested_user.state,
147144
postal_code=interested_user.postal_code,
148145
country=interested_user.country,
146+
phone=interested_user.phone,
149147
error_codes=error_codes
150148
), 200
151149

@@ -161,6 +159,14 @@ def put_interested_user_address_update(body):
161159
message="Invalid user."
162160
), 404
163161
else:
162+
required_fields = ['address_1', 'city', 'state', 'postal', 'phone']
163+
for f in required_fields:
164+
if body.get(f, "") == "":
165+
return jsonify(
166+
code=400,
167+
message="Failed to update address due to missing fields."
168+
), 400
169+
164170
with Transaction() as t:
165171
i_u_repo = InterestedUserRepo(t)
166172
interested_user = \
@@ -282,3 +288,17 @@ def _validate_user_match(interested_user, email):
282288
# someone doesn't stumble upon a valid email and/or id.
283289
# if they don't both match, treat as invalid
284290
return interested_user.email == email
291+
292+
293+
def search_ffq_codes_by_email(email, token_info):
294+
validate_admin_access(token_info)
295+
296+
with Transaction() as t:
297+
pfr = PerkFulfillmentRepo(t)
298+
ffq_diag = pfr.get_ffq_codes_by_email(email)
299+
ffq_codes_obj = {
300+
"ffq_codes": ffq_diag
301+
}
302+
if ffq_diag is None:
303+
return jsonify(code=404, message="Email not found"), 404
304+
return jsonify(ffq_codes_obj), 200

microsetta_private_api/api/_sample.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,13 @@ def update_sample_association(account_id, source_id, sample_id, body,
137137
except ValueError:
138138
raise BadRequest("Invalid sample_datetime")
139139
curdate = datetime.now(sample_datetime.tzinfo)
140-
lower_limit = curdate + relativedelta(years=-10)
140+
lower_limit = curdate + relativedelta(years=-1)
141141
upper_limit = curdate + relativedelta(months=+1)
142-
if sample_datetime < lower_limit or sample_datetime > upper_limit:
142+
is_admin = token_grants_admin_access(token_info)
143+
144+
# Allow admins to bypass the back-dating/forward-dating limits
145+
if (sample_datetime < lower_limit or sample_datetime > upper_limit)\
146+
and not is_admin:
143147
raise BadRequest('Invalid sample date')
144148
# sample_site will not be present if its environmental. this will
145149
# default to None if the key is not present
@@ -151,7 +155,6 @@ def update_sample_association(account_id, source_id, sample_id, body,
151155
body["sample_notes"]
152156
)
153157

154-
is_admin = token_grants_admin_access(token_info)
155158
sample_repo.update_info(account_id, source_id, sample_info,
156159
override_locked=is_admin)
157160

0 commit comments

Comments
 (0)