RSS Git Download  Clone
Raw Blame History
use strict;

# requires case to be reported, with diagnoses in lymphoid category
# also requires sql.lib file with 3 sql queries required by PrognosticIndicator

#------------------------------------------------------------------
package PrognosticIndicator; 
# provides necessary env for M::HMRN::PrognosticIndicator

use lib 'lib';
use LIMS::Local::QueryLibrary; # loads 'private' sql src file so can't use M::Roles::Query
use Moose;
with (
	'LIMS::Model::Roles::DBIxSimple', # provides lims_dbix() for M::HMRN::PrognosticIndicator
	'LIMS::Model::HMRN::PrognosticIndicator',
);
has _patient_id => ( is => 'rw', isa => 'Int'); 
has sql_lib     => ( is => 'ro', isa => 'LIMS::Local::QueryLibrary', lazy_build => 1 );
has lims_cfg    => ( is => 'ro', isa => 'HashRef', lazy_build => 1 );

sub patient_id { return shift->_patient_id }

sub _build_lims_cfg { LIMS::Local::Config->instance; }

sub _build_sql_lib {
    my $sql_lib = new LIMS::Local::QueryLibrary( { lib => 't/local.sql' } );
    return $sql_lib;
}

1;
#------------------------------------------------------------------

package main;

use Test::Builder::Tester;

use Data::Dumper;

use strict;
use warnings;

use Test::More tests => 39;
# use Test::More 'no_plan';

BEGIN {
    require 't/test-lib.pl';
}

our ($result);

my $dbix = get_dbix(); 

my $o = PrognosticIndicator->new(); # warn Dumper $o;

# rebuild tables:
my $sql = $o->sql_lib->retr('hmrn_rebuild_tables'); # warn Dumper $sql;
$sql =~ s/\n//g; # remove new-lines
my @tbls = split ';', $sql;
$dbix->dbh->do($_) for @tbls; # warn Dumper $_ for (split ";\n", $sql);

my @site_opts = (
    [ 'mesenteric', 'lower_nodal' ], # 1
    [ 'spleen',     'lower_nodal' ], # 2
    [ 'mediastinal','upper_nodal' ], # 3
    [ 'waldeyer',   'upper_nodal' ], # 4
    [ 'thymus',     'upper_nodal' ], # 5
    [ 'cns',        'extranodal'  ], # 6
    [ 'git',        'extranodal'  ], # 7
    [ 'marrow',     'extranodal'  ], # 8
    [ 'bulky',      'flag'        ], # 9
);

# populate tables:
_populate_tables();

# some default data; tested params =
# ldh, ecog, ct(scan), sweats, wt_loss, fever, extramedullary (sites hashref)
# albumin, lymphs, wbc, hb, plts
my %data = (
	lymphoid => {
        wbc 	=> 10.2,
        ecog 	=> 1,
        plts 	=> 317,
        wt_loss => 'N',
        fever 	=> 'N',
        sweats 	=> 'Y',
        ct 		=> 'Y', # required by almost all
        albumin => 41,
        hb 		=> 12.7,
        ldh 	=> 'normal',
        lymphs 	=> 5.0,
    },
    site_opts => { 6 => 1 }, # just cns
	imaging_opts => { $dbix->select('imaging_options',
		[ qw(id description option_type) ])->map_hashes('id') },
); # warn Dumper \%data;

{ # Ann Arbour:
    _change_diagnosis('FCL'); # change request #1 diagnosis
 
    # default data
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ann_arbor}, 'IEB'); # warn Dumper $result;
    
    # remove B-symptoms:
    local $data{lymphoid}{sweats} = 'N'; 
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ann_arbor}, 'IE'); # warn Dumper $result;
    
    # remove all extramedullary involvement:
    local $data{site_opts} = {};
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ann_arbor}, 'I'); # warn Dumper $result;
    
    # add 2 upper-nodal groups:
    local $data{site_opts}{5} = 1; # thymus
    local $data{site_opts}{4} = 1; # waldeyer
    $result = _get_prognostic_indicators(\%data); # warn Dumper \%data;
    is($result->{ann_arbor}, 'II[2]'); # warn Dumper $result;

    # add lower-nodal group:
    local $data{site_opts}{1} = 1; # mesenteric 
    $result = _get_prognostic_indicators(\%data); # warn Dumper \%data; 
    is($result->{ann_arbor}, 'III'); # warn Dumper $result;

    # add marrow involvment:
    local $data{site_opts}{8} = 1; # marrow
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ann_arbor}, 'IV'); # warn Dumper $result;

    # add spleen:
    local $data{site_opts}{2} = 1; # spleen
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ann_arbor}, 'IVS'); # warn Dumper $result;

    # add bulky disease:
    local $data{site_opts}{9} = 1; # bulky
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ann_arbor}, 'IVSX'); # warn Dumper $result;
    
    # no ann_arbor:
    local $data{lymphoid}{ct} = 'N'; 
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ann_arbor}, undef); # warn Dumper $result;
}

{ # FLIPI:    
    # default data (because age > 60):
    $result = _get_prognostic_indicators(\%data);
    is($result->{flipi}, 'good [1]'); # warn Dumper $result;

    { # local change to diagnosed date to take age < 60:
        # _manipulate_diagnosis_date_by_years(15); # changes with time
		_manipulate_patient_dob(58); # more robust for future dates
        $result = _get_prognostic_indicators(\%data);    
        is($result->{flipi}, 'good [0]'); # warn Dumper $result;
        # _manipulate_diagnosis_date_by_years(0); # '0' to revert
		_manipulate_patient_dob(75); # restore to original; more robust for future dates
    }

    # reduce Hb below 12:
    local $data{lymphoid}{hb} = 11;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{flipi}, 'intermediate [2]'); # warn Dumper $result;
    
    # increased ldh:
    local $data{lymphoid}{ldh} = 'raised';
    $result = _get_prognostic_indicators(\%data);    
    is($result->{flipi}, 'poor [3]'); # warn Dumper $result;
    
    # force Ann Arbour to stage IV:
    local $data{site_opts}{8} = 1; # marrow
    $result = _get_prognostic_indicators(\%data);        
    is($result->{flipi}, 'poor [4]'); # warn Dumper $result;
    
	# make upper_nodal + lower_nodal groups > 4:
	local $data{site_opts}{1} = 1; # mesenteric
    local $data{site_opts}{2} = 1; # spleen 
    local $data{site_opts}{3} = 1; # mediastinal
    local $data{site_opts}{4} = 1; # waldeyer
    local $data{site_opts}{5} = 1; # thymus 
    $result = _get_prognostic_indicators(\%data);        
    is($result->{flipi}, 'poor [5]'); # warn Dumper $result;   
    
    # no flipi:
    local $data{lymphoid}{ct} = 'N'; 
    $result = _get_prognostic_indicators(\%data);    
    is($result->{flipi}, undef); # warn Dumper $result;
}

{ # IPI:
    _change_diagnosis('DLBCL'); # change request #1 diagnosis
	local $data{site_opts} = {}; # clear nodal/extranodal involvement
	
    # default data
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ipi}, 'low [0]'); # warn Dumper $result;
    
    # increase ECOG score:
    local $data{lymphoid}{ecog} = 2; 
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ipi}, 'low-intermediate [1]'); # warn Dumper $result;
    
    # increased ldh:
    local $data{lymphoid}{ldh} = 'raised';
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ipi}, 'high-intermediate [2]'); # warn Dumper $result;

    # force Ann Arbour to stage IV:
    local $data{site_opts}{8} = 1; # marrow
    $result = _get_prognostic_indicators(\%data); # warn Dumper \%data;        
    is($result->{ipi}, 'high [3]'); 

    # $age > 60 && extranodal sites > 1:
    local $data{site_opts}{7} = 1; # git; 2nd extranodal site
    $result = _get_prognostic_indicators(\%data);        
    is($result->{ipi}, 'high [4]'); # warn Dumper $result;

    # decrease ECOG score to 0:
    local $data{lymphoid}{ecog} = 0; 
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ipi}, 'high [3]'); # warn Dumper $result;
    
    { # local change to diagnosed date to take age < 60:
        # _manipulate_diagnosis_date_by_years(15); # changes with time
		_manipulate_patient_dob(58); # more robust for future dates
        $result = _get_prognostic_indicators(\%data);    
        is($result->{ipi}, 'high-intermediate [2]'); # warn Dumper $result;
        # _manipulate_diagnosis_date_by_years(0); # '0' to revert
		_manipulate_patient_dob(75); # restore to original; more robust for future dates
    }

    # no ipi:
    local $data{lymphoid}{ecog} = undef; 
    $result = _get_prognostic_indicators(\%data);    
    is($result->{ipi}, undef); # warn Dumper $result;    
}

{ # HIPI:
    _change_diagnosis('Hodgkin'); # change request #1 diagnosis
    
    # default data ($age >= 45 & Gender = M):
    $result = _get_prognostic_indicators(\%data);    
    is($result->{hipi}, 'low-risk [2]'); # warn Dumper $result; exit;

    { # local change to diagnosed date to take age < 45:
        # _manipulate_diagnosis_date_by_years(30); # changes with time
		_manipulate_patient_dob(43); # more robust for future dates
        $result = _get_prognostic_indicators(\%data); # warn Dumper \%data;
        is($result->{hipi}, 'low-risk [1]'); # warn Dumper $result;
        # _manipulate_diagnosis_date_by_years(0); # '0' to revert
		_manipulate_patient_dob(75); # restore to original; more robust for future dates
    }
    
    # reduce Hb below 10.5:
    local $data{lymphoid}{hb} = 10;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{hipi}, 'mod/high risk [3]'); # warn Dumper $result;
    
    # increase wbc above 15:
    local $data{lymphoid}{wbc} = 16;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{hipi}, 'mod/high risk [4]'); # warn Dumper $result;

    # reduce albumin below 40:
    local $data{lymphoid}{albumin} = 30;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{hipi}, 'mod/high risk [5]'); # warn Dumper $result;
    
    # reduce lymphs below 0.6:
    local $data{lymphoid}{lymphs} = 0.5;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{hipi}, 'mod/high risk [6]'); # warn Dumper $result;

    # force Ann Arbour to stage IV:
    local $data{site_opts}{8} = 1; # marrow
    $result = _get_prognostic_indicators(\%data);        
    is($result->{hipi}, 'mod/high risk [7]'); # warn Dumper $result;
    
    # no hipi:
    local $data{lymphoid}{albumin} = undef; # warn Dumper \%data;
    $result = _get_prognostic_indicators(\%data);        
    is($result->{hipi}, undef); # warn Dumper $result;    
}

{ # Binet:
    _change_diagnosis('B-LPD'); # change request #1 diagnosis
    
    # default data
    $result = _get_prognostic_indicators(\%data);    
    is($result->{binet}, 'A'); # warn Dumper $result;
    
    # add upper nodal (stays at 'A'):
    local $data{site_opts}{5} = 1; # thymus
    $result = _get_prognostic_indicators(\%data);    
    is($result->{binet}, 'A'); # warn Dumper $result;
    
    # add lower nodal:
    local $data{site_opts}{1} = 1; # mesenteric
    $result = _get_prognostic_indicators(\%data);    
    is($result->{binet}, 'A'); # warn Dumper $result;

    # add another upper nodal:
    local $data{site_opts}{4} = 1; # waldeyer
    $result = _get_prognostic_indicators(\%data);    
    is($result->{binet}, 'B'); # warn Dumper $result;
    
    # reduce Hb < 10: || $plts < 100) { $binet = 'C' }
    local $data{lymphoid}{hb} = 9;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{binet}, 'C'); # warn Dumper $result;
    
    # restore Hb  & reduce $plts < 100:
    local $data{lymphoid}{hb} = 11;
    local $data{lymphoid}{plts} = 80;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{binet}, 'C'); # warn Dumper $result;
    
    # no Binet:
    local $data{lymphoid}{plts} = undef;
    $result = _get_prognostic_indicators(\%data);    
    is($result->{binet}, undef); # warn Dumper $result;    
}

sub _get_prognostic_indicators {
    my $data = shift; # warn Dumper $data;
    
    # create new PrognosticIndicator object to force diagnostic_categories_map change:
    my $o = PrognosticIndicator->new(
        lims_dbix => $dbix,
        _patient_id => 1,
    ); # warn Dumper $o;
	
	# LIMS::Model::HMRN::PrognosticIndicator requires:
		# params_data - HoH containing 'lymphoid' accessor
		# initial_scan - HoH containing 'site_opts' accessor
		# option_maps - {imaging_options}->{map}
	my %params = ( lymphoid => $data->{lymphoid} );
	my %i_scan = ( site_opts => $data->{site_opts} );
	my %o_maps = ( imaging_options => { map => $data->{imaging_opts} } );
	
    my %h = (
        params_data  => \%params,
        initial_scan => \%i_scan,
        option_maps  => \%o_maps,
    ); # warn Dumper \%h;

    my $result = $o->prognostic_indicators(\%h);
    return $result;
}

=begin - repalced with dob manipulation - future-proof
sub _manipulate_diagnosis_date_by_years {
    my $years = shift;
    
    $dbix->update('patient_event', 
        { date => DateTime->today->subtract( years => $years )->ymd },
        { patient_id => 1, event_id => 1 }
    );
}
=cut
sub _manipulate_patient_dob {
    my $years = shift;
    
    $dbix->update('lims_test.patients', 
        { dob => DateTime->today->subtract( years => $years )->ymd },
        { id => 1 }
    );	
}

sub _populate_tables {
    my $dbh = $dbix->dbh;
    
    { # imaging site options (just need a selection):
        for (@site_opts) {
            $dbh->do(
                q!INSERT INTO imaging_options (description,option_type) VALUES (?,?)!,
                undef, @$_
            );
        }
        for (1 .. scalar @site_opts) {
            $dbh->do( q!INSERT INTO patient_imaging_option VALUES (?,?)!, undef, 1, $_ );        
        }
    }
    { # events 
        $dbh->do(
                qq!INSERT INTO events (id, description) VALUES (?,?)!,
                undef, 1, 'diagnosis' );
    }
    { # diagnostic categories:
        my $categories = _get_diagnostic_categories();
        for (@$categories) {
            $dbh->do(
                qq!INSERT INTO diagnostic_categories (description,category_type)
                    VALUES (?,?)!, undef, $_, 'sub'
            );
        }
    }
    { # diagnoses:
        my $diagnoses = _get_diagnoses();
        my $sql = 'select id from diagnostic_categories where description = ?';
        while ( my($name,$icdo3) = each %$diagnoses ) { # warn Dumper [$name,$icdo3];
            # get diagnostic_category_id:
            my $category = $name; # except:
            $category =~ s/B-LPD/CLL/; 
            $category =~ s/FCL/FL/;
            
            $dbix->query($sql, $category)->into(my $id);
            $dbix->insert('diagnoses', {
                name => $name, icdo3 => $icdo3, diagnostic_category_id => $id,
            });
        }
    }
    { # icdo3_category (simplified version of hilis4):
        $dbix->dbh->do(
            q!insert into icdo_category(diagnostic_category_id,icdo3)
                select diagnostic_category_id,icdo3 from diagnoses!
        );
    }
    { # patient imaging event:        
        my %h = (
            patient_id => 1, dataset => 1, scan_type => 'ct',
            stage => 'initial', date => DateTime->today->ymd,
        );
        $dbix->insert('patient_imaging_event', \%h);
    }
    { # patient event:
        $dbix->insert('patient_event',
            { patient_id => 1, event_id => 1, date => DateTime->today->ymd }
        );
    }
}

sub _change_diagnosis {
    my $diagnosis = shift;
    
    $dbix->query('select id from diagnoses where name = ?', $diagnosis)->into(my $id);
    
    $dbix->delete('request_report_detail', { request_id => 1 });
    $dbix->insert('request_report_detail', { request_id => 1, diagnosis_id => $id });    
}

sub _get_diagnoses {
    return {
        'B-LPD' => '9823/3', # already have CLL as Lymphoproliferative
        FCL     => '9690/3',
        MZL     => '9689/3', # SMZL
        Hodgkin => '9652/3',
        DLBCL   => '9680/3',
        MCL     => '9673/3',
    }
}

sub _get_diagnostic_categories {
    return [ qw(CLL FL MZL Hodgkin DLBCL MCL) ];
}

# perlfaq 4 - How do I find the first array element for which a condition is true?
sub _get_array_position_for {
    my ($term, $array) = @_; # warn Dumper [$term, $array];
    
    my $index = -1; 
	for ( my $i = 0; $i < @$array; $i++ ) {
		if ( $array->[$i] eq $term ) { # want exact match in this case
			$index = $i; # warn $index;
			last;  
		}
	}
    return $index;
}