Skip to content

Commit 25c215f

Browse files
attempted changes to create test users and applications
1 parent d708e3a commit 25c215f

File tree

9 files changed

+160
-84
lines changed

9 files changed

+160
-84
lines changed

apps/testclient/management/commands/create_test_users_and_applications_batch.py

Lines changed: 106 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import apps.logging.request_logger as logging
77

8-
from datetime import datetime, timedelta
8+
from datetime import datetime, timedelta, timezone
99
from os import listdir
1010
from random import randint, randrange, choice, sample
1111
from django.contrib.auth.models import Group
@@ -15,6 +15,7 @@
1515
from django.conf import settings
1616
from django.utils import timezone
1717
from oauth2_provider.models import AccessToken, RefreshToken
18+
from tqdm import tqdm
1819

1920
from apps.accounts.models import UserProfile
2021
from apps.fhir.bluebutton.models import Crosswalk
@@ -34,7 +35,9 @@
3435
'capability-a', 'capability-b']
3536
APPLICATION_SCOPES_NON_DEMOGRAPHIC = ['patient/ExplanationOfBenefit.read',
3637
'patient/Coverage.read', 'capability-a', 'capability-b']
37-
DEFAULT_BENE_COUNT = 100
38+
39+
# Keep up with prod count
40+
DEFAULT_BENE_COUNT = 675000
3841
DEFAULT_DEV_COUNT = 150
3942
DEFAULT_MAX_APPS_PER_DEV = 5
4043

@@ -60,92 +63,114 @@ def get_first_available_number(firstname):
6063
return int(begin) + 1
6164

6265
def create_dev_users_apps_and_bene_crosswalks(
63-
group,
6466
bene_count,
6567
dev_count,
6668
app_max,
6769
refresh_count,
6870
archived_tokens,
69-
archived_grants
71+
archived_grants,
72+
auto_generate
7073
):
71-
#
72-
# generate dev users dev0001, dev0002, dev0003 etc, with password, email, security questions, etc.
73-
# Counts are based on the inputs, but if undefined,will default to the defaults defined above
74-
# each dev user can have 1 to many applications: dev0001-app01, dev0001-app02
75-
# generate crosswalk bene users with FHIR-ID. HICN-HASH, MBI-HASH etc
76-
#
77-
78-
# effectively turn off logging
74+
"""Creates bene and dev users, associated apps, and crosswalks for testing
75+
76+
Args:
77+
bene_count (_type_): _description_
78+
dev_count (_type_): _description_
79+
app_max (_type_): _description_
80+
refresh_count (_type_): _description_
81+
archived_tokens (_type_): _description_
82+
archived_grants (_type_): _description_
83+
auto_generate (_type_): _description_
84+
"""
7985
mymedicare_cb_logger.setLevel(logging.CRITICAL)
8086
outreach_logger.setLevel(logging.CRITICAL)
8187

88+
group = create_group()
8289
bene_pk_list = []
83-
files = [f for f in listdir('./synthetic-data') if re.match(r'synthetic-beneficiary-.*\.rif', f)]
90+
8491
count = 0
8592
file_cnt = 0
8693
synthetic_bene_cnt = 0
8794
begin_number = get_first_available_number('bene')
88-
89-
for f in files:
90-
print('file={}'.format(f))
91-
bene_rif = open('./synthetic-data/{}'.format(f), 'r')
92-
while True:
93-
if count == bene_count:
94-
break
95-
line = bene_rif.readline()
96-
if not line:
95+
if auto_generate:
96+
for i in tqdm(range(bene_count)):
97+
id = str(i).zfill(10)
98+
fn = f'FN{id}'
99+
ln = f'LN{id}'
100+
fhir_id = f'-{id}'
101+
args = {
102+
'username': str(uuid.uuid4()),
103+
'user_hicn_hash': hash_hicn(id),
104+
'user_mbi': f'MBI{id}',
105+
'user_id_type': 'H',
106+
'fhir_id_v2': fhir_id,
107+
'first_name': fn,
108+
'last_name': ln,
109+
'email': f'{fn}.{ln}@xyz.com'
110+
}
111+
slsx_client = OAuth2ConfigSLSx(args)
112+
user = create_beneficiary_record(slsx_client, fhir_id)
113+
bene_pk_list.append(user.pk)
114+
else:
115+
files = [f for f in listdir('./synthetic-data') if re.match(r'synthetic-beneficiary-.*\.rif', f)]
116+
for f in files:
117+
print('file={}'.format(f))
118+
bene_rif = open('./synthetic-data/{}'.format(f), 'r')
119+
while True:
120+
if count == bene_count:
121+
break
122+
line = bene_rif.readline()
123+
if not line:
124+
break
125+
if line.startswith('DML_IND|BENE_ID'):
126+
continue
127+
else:
128+
flds = line.split('|')
129+
fhir_id = flds[1]
130+
mbi = flds[14]
131+
hicn = flds[18]
132+
fn = f'bene{count + begin_number}'
133+
ln = f'user{count + begin_number}'
134+
135+
# skip fred
136+
if fhir_id != '-20140000008325':
137+
args = {
138+
'username': str(uuid.uuid1()),
139+
'user_hicn_hash': hash_hicn(hicn),
140+
'user_mbi': mbi,
141+
'user_id_type': 'H',
142+
'fhir_id_v2': fhir_id,
143+
'first_name': fn,
144+
'last_name': ln,
145+
'email': fn + '.' + ln + '@xyz.net',
146+
}
147+
slsx_client = OAuth2ConfigSLSx(args)
148+
try:
149+
u = create_beneficiary_record(slsx_client, fhir_id)
150+
date_picked = datetime.now(timezone.utc) - timedelta(days=randrange(700))
151+
u.date_joined = date_picked.replace(tzinfo=pytz.utc)
152+
u.save()
153+
c = Crosswalk.objects.get(user=u)
154+
c.created = u.date_joined
155+
c.save()
156+
bene_pk_list.append(u.pk)
157+
synthetic_bene_cnt += 1
158+
count += 1
159+
except ValidationError:
160+
# If there is something wrong during 'create_beneficiary_record'
161+
# i.e. 'user already exists', just try the next .rif record
162+
continue
163+
bene_rif.close()
164+
file_cnt += 1
165+
print('RIF file processed = {}, synthetic bene generated = {}'.format(file_cnt, synthetic_bene_cnt))
166+
if file_cnt >= 1:
97167
break
98-
if line.startswith('DML_IND|BENE_ID'):
99-
continue
100-
else:
101-
flds = line.split('|')
102-
fhir_id = flds[1]
103-
mbi = flds[14]
104-
hicn = flds[18]
105-
fn = 'bene{}'.format(count + begin_number)
106-
ln = 'user{}'.format(count + begin_number)
107-
108-
# skip fred
109-
if fhir_id != '-20140000008325':
110-
args = {
111-
'username': str(uuid.uuid1()),
112-
'user_hicn_hash': hash_hicn(hicn),
113-
'user_mbi': mbi,
114-
'user_id_type': 'H',
115-
'fhir_id_v2': fhir_id,
116-
'first_name': fn,
117-
'last_name': ln,
118-
'email': fn + '.' + ln + '@xyz.net',
119-
}
120-
slsx_client = OAuth2ConfigSLSx(args)
121-
try:
122-
u = create_beneficiary_record(slsx_client, fhir_id)
123-
date_picked = datetime.utcnow() - timedelta(days=randrange(700))
124-
u.date_joined = date_picked.replace(tzinfo=pytz.utc)
125-
u.save()
126-
c = Crosswalk.objects.get(user=u)
127-
c.created = u.date_joined
128-
c.save()
129-
bene_pk_list.append(u.pk)
130-
synthetic_bene_cnt += 1
131-
count += 1
132-
print('.', end='', flush=True)
133-
time.sleep(.05)
134-
except ValidationError:
135-
# If there is something wrong during 'create_beneficiary_record'
136-
# i.e. 'user already exists', just try the next .rif record
137-
continue
138-
bene_rif.close()
139-
file_cnt += 1
140-
print('RIF file processed = {}, synthetic bene generated = {}'.format(file_cnt, synthetic_bene_cnt))
141-
if file_cnt >= 1:
142-
break
143168

144169
# create dev users according dev-count parameter, default to 100
145170
# generate access tokens + refresh tokens + archived tokens for random picked benes for each app
146171
app_index = 0
147172
begin_number = get_first_available_number('DevUserFN')
148-
for i in range(dev_count):
173+
for i in tqdm(range(dev_count)):
149174
dev_u_fn = 'DevUserFN{}'.format(i + begin_number)
150175
dev_u_ln = 'DevUserLN{}'.format(i + begin_number)
151176
u = User.objects.create_user(username='{}.{}'.format(dev_u_fn, dev_u_ln),
@@ -166,7 +191,6 @@ def create_dev_users_apps_and_bene_crosswalks(
166191
u.groups.add(group)
167192
# apps per DEV user
168193
app_cnt = randint(1, app_max) if app_max > 0 else 0
169-
print('>>>>generating apps for user={}'.format(u.username))
170194
for i in range(app_cnt):
171195
app_index += 1
172196
app_name = 'app{}_{}'.format(i, u)
@@ -205,7 +229,6 @@ def create_dev_users_apps_and_bene_crosswalks(
205229
for t in titles:
206230
c = ProtectedCapability.objects.get(title=t)
207231
a.scope.add(c)
208-
print('<<<<<generated apps for user={}'.format(u.username))
209232

210233
# go through benes: each bene sign up to 1, 2, 3 apps
211234
# most 70% 1 app, 25% 2 apps, 5% 3 apps
@@ -274,14 +297,12 @@ def create_test_access_refresh_archived_objects(
274297
scope=scope)
275298
at.created = date_created.replace(tzinfo=pytz.utc)
276299
at.save()
277-
print('<<< access token created for ' + user.username + '/' + application.name)
278300

279301
for i in range(refresh_count):
280302
rt = RefreshToken.objects.create(user=user, application=application,
281303
token=uuid.uuid4().hex)
282304
rt.created = at.created
283305
rt.save()
284-
print('<<< ' + user.username + ' refresh token ' + str(i) + ' generated')
285306

286307
# archived token: created, updated, archived_at datetime fields
287308
for i in range(archived_token_count):
@@ -297,7 +318,6 @@ def create_test_access_refresh_archived_objects(
297318
date_archived = ot.created + timedelta(days=10)
298319
ot.archived_at = date_archived.replace(tzinfo=pytz.utc)
299320
ot.save()
300-
print('<<< ' + user.username + ' archived token ' + str(i) + ' generated')
301321

302322
past_date = timezone.now() - timedelta(days=2)
303323
for i in range(archived_grant_count):
@@ -308,8 +328,11 @@ def create_test_access_refresh_archived_objects(
308328
archived_at=past_date)
309329
past_date = past_date - timedelta(days=2)
310330
adag.save()
311-
print('<<< ' + user.username + 'archived grant ' + str(i) + ' generated')
312331

332+
def delete_users_apps_crosswalks():
333+
User.objects.all().delete()
334+
Application.objects.all().delete()
335+
Crosswalk.objects.all().delete()
313336

314337
class Command(BaseCommand):
315338
help = ('Create dev users and create'
@@ -333,6 +356,8 @@ def add_arguments(self, parser):
333356
parser.add_argument('-g', '--archived-access-grants', default=0,
334357
help='Archived access grants per user/app combination. '
335358
'If none, defaults to 0.')
359+
parser.add_argument('--auto-generate', default=False, help='Do not prompt for user input.')
360+
parser.add_argument('--delete', default=False, help='Delete all test users and applications.')
336361

337362
def handle(self, *args, **options):
338363
bene_count = int(options['bene_count'])
@@ -341,14 +366,18 @@ def handle(self, *args, **options):
341366
refresh_tokens = int(options['refresh_tokens'])
342367
archived_tokens = int(options['archived_tokens'])
343368
archived_grants = int(options['archived_access_grants'])
344-
g = create_group()
369+
auto_generate = bool(options['auto_generate'])
370+
delete = bool(options['delete'])
371+
372+
if delete:
373+
delete_users_apps_crosswalks()
345374
create_dev_users_apps_and_bene_crosswalks(
346-
g,
347375
bene_count,
348376
dev_count,
349377
app_max,
350378
refresh_tokens,
351379
archived_tokens,
352-
archived_grants)
380+
archived_grants,
381+
auto_generate)
353382
# update grants
354383
update_grants()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from django.core.management.base import BaseCommand
2+
from django.contrib.auth.models import User
3+
from oauth2_provider.models import AccessToken, RefreshToken
4+
from apps.fhir.bluebutton.models import Crosswalk
5+
from apps.dot_ext.models import ArchivedToken
6+
from apps.capabilities.models import ProtectedCapability
7+
from apps.authorization.models import update_grants, ArchivedDataAccessGrant
8+
from oauth2_provider.models import get_application_model
9+
10+
11+
def flush_database():
12+
"""Flush all data from the database."""
13+
print('Flushing database...')
14+
User.objects.all()
15+
get_application_model().objects.all()
16+
Crosswalk.objects.all()
17+
ProtectedCapability.objects.all()
18+
AccessToken.objects.all()
19+
RefreshToken.objects.all()
20+
ArchivedToken.objects.all()
21+
ArchivedDataAccessGrant.objects.all()
22+
print('Database flushed.')
23+
24+
class Command(BaseCommand):
25+
help = ('Create dev users and create'
26+
' apps for each of them, create bene users from '
27+
'synthetic data and crosswalk for each bene.')
28+
29+
def add_arguments(self, parser):
30+
parser.add_argument('--flush', default=False, help='Flush the database')
31+
32+
def handle(self, *args, **options):
33+
delete = bool(options['delete'])
34+
35+
if delete:
36+
flush_database()
37+
38+
# update grants
39+
update_grants()

requirements/requirements.dev.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ selenium
1111
pytest
1212
Flask
1313
Flask-WTF
14+
typing_extensions>=4.6.0
1415
setuptools_scm<7,>=3.2
1516
setuptools>=40.8.0
17+
tqdm

requirements/requirements.dev.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,10 @@ tomli==2.2.1 \
866866
--hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \
867867
--hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7
868868
# via setuptools-scm
869+
tqdm==4.67.1 \
870+
--hash=sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2 \
871+
--hash=sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2
872+
# via -r requirements/requirements.dev.in
869873
trio==0.24.0 \
870874
--hash=sha256:c3bd3a4e3e3025cd9a2241eae75637c43fe0b9e88b4c97b9161a55b9e54cd72c \
871875
--hash=sha256:ffa09a74a6bf81b84f8613909fb0beaee84757450183a7a2e0b47b455c0cac5d
@@ -876,10 +880,11 @@ trio-websocket==0.11.1 \
876880
--hash=sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f \
877881
--hash=sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638
878882
# via selenium
879-
typing-extensions==4.5.0 \
880-
--hash=sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb \
881-
--hash=sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4
883+
typing-extensions==4.15.0 \
884+
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
885+
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
882886
# via
887+
# -r requirements/requirements.dev.in
883888
# -r requirements/requirements.in
884889
# dj-database-url
885890
# jwcrypto

requirements/requirements.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pyyaml
3737
py
3838
attrs
3939
pyrsistent
40-
typing_extensions
40+
typing_extensions>=4.6.0
4141
importlib-metadata
4242
certifi
4343

requirements/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,9 +638,9 @@ sqlparse==0.5.0 \
638638
# via
639639
# -r requirements/requirements.in
640640
# django
641-
typing-extensions==4.5.0 \
642-
--hash=sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb \
643-
--hash=sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4
641+
typing-extensions==4.15.0 \
642+
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
643+
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
644644
# via
645645
# -r requirements/requirements.in
646646
# dj-database-url
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
BENE_HICN, BENE_MBI, BENE_FHIR, BENE_FN, BENE_LN
76.7 KB
Binary file not shown.
43.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)