#!/usr/bin/env perl # # This script will populate an iAbetic drupal database with some fake patient data. # # It takes one argument: the connection string for a mysql connection to the database in which to inject data. # # The script is kind of basic, and the data it generates may not be especially interesting or real. # It just picks random foods and pull B-Gl numbers out of thin air. # use strict; use Data::Faker; use Data::Dumper; use DBI; use Getopt::Simple; use WWW::Mechanize; use Date::Manip; my($options) = { help => { type => '', env => '-', default => '', order => 1, }, hostname => { type => '=s', env => '-', default => 'localhost', verbose => 'Specify the hostname to connect to', order => 2, }, db_user => { type => '=s', # As per Getopt::Long. env => '$USER', # Help text. default => $ENV{'USER'}, # In case $USER is undef. verbose => 'Specify the username on the remote machine', order => 3, # Help text sort order. }, db_passwd => { type => '=s', env => '-', default => '', verbose => 'Specify the password on the remote machine', order => 4, }, database => { type => '=s', env => '-', default => 'drupaldb', verbose => 'Specify the name of the database to connect to', order => 5, }, drupal_user => { type => '=s', # As per Getopt::Long. env => '$USER', # Help text. default => $ENV{'USER'}, # In case $USER is undef. verbose => 'Specify the username on the remote machine', order => 3, # Help text sort order. }, drupal_passwd => { type => '=s', env => '-', default => '', verbose => 'Specify the password on the remote machine', order => 4, }, num_patients => { type => '=i', env => '-', default => 1, verbose => 'Specify the number of fake patients to create', order => 6, }, num_days => { type => '=i', env => '-', default => 5, verbose => 'Specify the number of days of data to create, counting back from today', order => 7, }, url_prefix => { type => '=s', env => '-', default => '/', verbose => 'Specify the URL prefix. That part after hostname, before "admin/user/user/create" in the URL to create the user at', order => 8, }, verbose => { type => '!', env => '-', default => 0, verbose => 'Dump extra information while running', order => 9, }, really_insert => { type => '!', env => '-', default => 1, verbose => 'Actually insert the data to database/drupal', order => 10, }, }; my($option) = Getopt::Simple -> new(); if (! $option -> getOptions($options, "Usage: make_fake_data.pl [options]", 0) ) { exit(-1); # Failure. } my $dsn = "DBI:mysql:database=$$option{'switch'}{'database'};host=$$option{'switch'}{'hostname'}"; my $dbh = DBI->connect($dsn, $$option{'switch'}{'db_user'}, $$option{'switch'}{'db_passwd'}) or die "Failed to connect to database"; my $create_account_sth = $dbh->prepare('insert into iab_account(creation_date) values(now())'); my $create_system_user_sth = $dbh->prepare('insert into iab_system_user(system_user_id,account_id) values (?,?)'); my $create_person_sth = $dbh->prepare('insert into iab_person(first_name,middle_name,last_name) values (?,?,?)'); my $create_patient_profile_sth = $dbh->prepare('insert into iab_patient_profile(type1_diabetic,diagnosis_date,account_id,person_id,gender,birth_date,notes) values (?,?,?,?,?,?,?)'); my $fia_event_create_sth = $dbh->prepare('insert into iab_fia_event(patient_profile_id, event_creator, creation_date, event_bgl, event_datetime, event_final_bgl) values (?,?,now(),?,?,?)'); my $fia_event_activity_create_sth = $dbh->prepare('insert into iab_fia_event_activity(event_id, activity_id, activity_amount) values (?,?,?)'); my $fia_event_food_create_sth = $dbh->prepare('insert into iab_fia_event_food(event_id, food_item_code, food_quantity_grams) values (?,?,?)'); my $fia_event_insulin_create_sth = $dbh->prepare('insert into iab_fia_event_insulin(event_id, insulin_type_id, insulin_quantity, injection_site) values (?,?,?,?)'); sub insert_fia_event($$) { my $patient_profile_id = shift; my $event = shift; print Dumper(\$event); } sub insert_fia_events($$) { my $patient_profile_id = shift; my $events = shift; foreach my $day_key (keys(%{$events})) { foreach my $event_key (keys(%{$events->{$day_key}})) { insert_fia_event($patient_profile_id, $events->{$day_key}->{$event_key}); } } } sub insert_patient($$) { my $uid = shift; my $patient = shift; my $patient_profile_id; if($$options{'switch'}{'really_insert'}) { $create_account_sth->execute(); my $account_id = $dbh->last_insert_id(undef,undef,undef,undef); $create_system_user_sth->execute($uid,$account_id); $create_person_sth->execute($patient->{name}{first},$patient->{name}{middle},$patient->{name}{last}); my $person_id = $dbh->last_insert_id(undef,undef,undef,undef); $create_patient_profile_sth->execute( $patient->{type1_diabetic}, $patient->{diagnosis_date}, $account_id, $person_id, $patient->{gender}, $patient->{birth_date}, 'Dummy patient created by make_fake_data script' ); $patient_profile_id = $dbh->last_insert_id(undef,undef,undef,undef); print "Patient profile id: $patient_profile_id\n"; } insert_fia_events($patient_profile_id, $patient->{days}); } # Create a patient with a name and info, and a drupal account sub make_patient() { my $faker = Data::Faker->new(); my $birth_date = $faker->sqldate; my $diagnosis_date = $faker->sqldate; while($birth_date ge $diagnosis_date) { $birth_date = $faker->sqldate; $diagnosis_date = $faker->sqldate; } my $patient = { name => { first => $faker->first_name, middle => $faker->first_name, last => $faker->last_name, }, type1_diabetic => int(rand(2)), diagnosis_date => $diagnosis_date, gender => (rand()<0.5 ? 'M' : 'F'), birth_date => $birth_date, }; $patient->{username} = "$patient->{name}{first}.$patient->{name}{last}"; $patient->{email} = "$patient->{username}\@example.com"; return $patient; } # Generate a blood glucose reading sub make_glucose_reading() { return int(rand(120)) + 70; } # Generate a meal made up of a number of individual food items my $meal_sth = $dbh->prepare("SELECT ndb_no,long_description from iab_sr21_food_values order by rand() limit ?"); sub make_meal($$) { my $min_foods = shift; my $max_foods = shift; my $num_foods = int(rand(1+$max_foods-$min_foods)) + $min_foods; $meal_sth->execute($num_foods); my $food_results = $meal_sth->fetchall_hashref('ndb_no'); return $food_results; } # Generate an insulin injection my $insulin_sth = $dbh->prepare("SELECT insulin_type_id, insulin_name, injection_site_id, iab_injection_site.name injection_site_name, floor(rand()*8+8) units from iab_insulin_type, iab_injection_site order by rand() limit 1"); sub make_insulin() { $insulin_sth->execute(); my $insulin_results = $insulin_sth->fetchall_hashref('insulin_type_id'); return $insulin_results; } # Generate an activity my $activity_sth = $dbh->prepare("SELECT activity_id,name,floor(rand()*6+2)*15 amount from iab_activities order by rand() limit 1"); sub make_activity() { $activity_sth->execute(); my $activity_results = $activity_sth->fetchall_hashref('activity_id'); return $activity_results; } # Generate a complete fia_event including food, insulin, and activity plus a blood glucose reading. sub make_fia_event($$$$) { my $days_ago = shift; my $hour_of_day = shift; my $min_foods = shift; my $max_foods = shift; return { event_datetime => UnixDate(ParseDate("$hour_of_day:00 $days_ago days ago"), '%O'), meal => make_meal($min_foods, $max_foods), insulin => make_insulin(), activity => make_activity(), glucose => make_glucose_reading(), }; } # Generate a complete day: 5 fia_events: 3 meals, 2 non-meals sub make_day($) { my $days_ago = shift; return { breakfast => make_fia_event($days_ago, 9, 1,2), elevensies => make_fia_event($days_ago, 11, 0,0), lunch => make_fia_event($days_ago, 13, 2,3), afternoon => make_fia_event($days_ago, 16, 0,0), dinner => make_fia_event($days_ago, 20, 2,3) }; } my (%patients,$patient_loop); for($patient_loop=1; $patient_loop <= $$option{'switch'}{'num_patients'}; $patient_loop++) { print "Generating patient $patient_loop\n"; $patients{"patient$patient_loop"} = make_patient(); my $days_loop; for($days_loop=1; $days_loop <= $$option{'switch'}{'num_days'}; $days_loop++) { print "Generating day $days_loop\n"; $patients{"patient$patient_loop"}{"days"}{"day$days_loop"} = make_day($days_loop); } } print Dumper(\%patients) if($$option{'switch'}{'verbose'}); my $mech = WWW::Mechanize->new(); # First log in to the Drupal system $mech->get('http://'.$$option{'switch'}{'hostname'}.$$option{'switch'}{'url_prefix'}); $mech->submit_form(with_fields => { name => $$option{'switch'}{'drupal_user'}, pass => $$option{'switch'}{'drupal_passwd'}, } ); # Now fetch the create new user page $mech->get('http://'.$$option{'switch'}{'hostname'}.$$option{'switch'}{'url_prefix'}.'admin/user/user/create'); my $get_uid_sth = $dbh->prepare("SELECT uid from users where name=?"); for($patient_loop=1; $patient_loop <= $$option{'switch'}{'num_patients'}; $patient_loop++) { print "Creating patient: ",$patients{"patient$patient_loop"}{name}{first}," ",$patients{"patient$patient_loop"}{name}{last},"\n"; my $uid=0; if($$options{'switch'}{'really_insert'}) { $mech->submit_form( form_id => 'user-register', fields => { name => $patients{"patient$patient_loop"}{username}."_fake", 'pass[pass1]' => $patients{"patient$patient_loop"}{username}."_pass", 'pass[pass2]' => $patients{"patient$patient_loop"}{username}."_pass", status => 1, mail => $patients{"patient$patient_loop"}{email}, first_name => $patients{"patient$patient_loop"}{name}{first}, middle_name => $patients{"patient$patient_loop"}{name}{middle}, last_name => $patients{"patient$patient_loop"}{name}{last}, account_type => 0, consent_form => 1, privacy => 1, contact_me => 1, race => -1, diabetes_type => $patients{"patient$patient_loop"}{type1_diabetes}+1, notes => 'Dummy patient created by make_fake_data script', }, ); # Now find the userid generated by drupal $get_uid_sth->execute($patients{"patient$patient_loop"}{username}."_fake"); $uid = $get_uid_sth->fetchrow_hashref->{uid}; print "Created as uid: $uid\n"; } insert_patient($uid, $patients{"patient$patient_loop"}); }