PerformanceData
package Athena::P4P::App::PerformanceData; use Athena::Policy; use Athena::Conf::AthenaNet; use Cache; use Global; use AthenaDate; use Athena::Util::Database; use Clinical::P4P::Provider; use Clinical::P4P::Log; use DateTime; use Athena::P4P::Persistence::QPP; use YAML::XS; use Try::Tiny; use Athena::Util::JSON; use Athena::P4P::Persistence::QPP::API; use Athena::Util::List qw(InList); use Athena::P4P::App::Rolling90Days; use SQL::Select; # This constant hashref does not use the MIPS_[A-Z]+_PROGRAM_ID constants # because we want to wean the code off of them. Ultimately this hashref # will go away too when the program relationships can be queried from # the database. # # DO NOT USE THIS CONSTANT DIRECTLY. # Call the wrapper functions GetMIPSProgram, GetMIPSProgramList, GetMIPSProgramYearList, # GetMIPSCategory, GetMIPSCategoryMap, GetMIPSProgramsByCategory, and GetMIPSEntityType instead. use constant _MIPS_PROGRAM_DATA => { 2017 => { ACI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Advancing Care Information (ACI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 3876, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 3119, }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 3878, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 3205, } }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 3880, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 3152, }, }, }, }, }, 2018 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 3877, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 3791, }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 3875, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 3796, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 3879, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 3797, }, }, }, }, }, 2019 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 4240, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 4236, }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 4237, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 4219, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 4194, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 4238, }, }, }, }, }, 2020 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 4704, }, NINETYDAYS => { P4PPROGRAMID => 5277, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 4683, }, NINETYDAYS => { P4PPROGRAMID => 5278, }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 4670, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 4682, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 4680, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 4660, }, }, }, }, }, 2021 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 5216, }, NINETYDAYS => { P4PPROGRAMID => "5617,5620,5621,5623", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 5239, }, NINETYDAYS => { P4PPROGRAMID => "5618,5619,5622,5624", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 5185, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 5230, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 5260, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 5215, }, }, }, }, }, 2022 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 5808, }, NINETYDAYS => { P4PPROGRAMID => "5907,5914,5919,5921,6400,6377,6265,6386,6398,6243,6403,6330,6342,6291,6339,6258,6379,6367,6415,6308,6407,6397,6418,6417,6387,6304,6358,6327,6279,6351,6277,6299,6406,6250,6337,6380,6354,6390,6378,6368,6385,6350,6347,6319,6315,6302,6411,6361,6284,6314,6375,6309,6324,6270,6345,6262,6261,6372,6248,6306,6249,6311,6334,6283,6329,6303,6266,6399,6323,6413,6384,6408,6335,6313,6281,6333,6297,6275,6253,6357,6370,6364,6376,6288,6289,6356,6414,6338,6272,6466,6463,6461,6464,6462,6460,6458,6465,6459,6467", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 5803, }, NINETYDAYS => { P4PPROGRAMID => "5910,5912,5915,5920,6325,6402,6352,6343,6396,6305,6366,6293,6355,6241,6273,6381,6373,6238,6316,6260,6393,6296,6346,6320,6405,6383,6328,6295,6239,6344,6267,6278,6286,6395,6326,6412,6321,6401,6240,6294,6388,6245,6404,6301,6058,6256,6371,6242,6392,6287,6247,6391,6254,6389,6365,6282,6382,6349,6300,6257,6374,6331,6271,6362,6264,6360,6322,6276,6251,6274,6341,6318,6336,6268,6252,6246,6259,6394,6410,6363,6292,6263,6285,6409,6353,6280,6298,6269,6312,6244,6310,6416,6307,6359,6317,6255,6332,6369,6348,6290,6340", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 5794, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 5799, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 5821, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 5824, }, }, }, }, }, 2023 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 6218, }, NINETYDAYS => { P4PPROGRAMID => "6099,6111,6140,6151,6659,6932,6933,6934,6935,6936,6937,6938,6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950,6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968,6969,6970,6971,6972,6973,6974,6975,6976,6977,6978,6979,6980,6981,6982,6983,6984,6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,6998,6999,7000,7001,7002,7003,7004,7005,7006,7007,7008,7009,7010,7011,7012,7013,7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 6191, }, NINETYDAYS => { P4PPROGRAMID => "6106,6135,6149,6213,6658,6844,6845,6846,6847,6848,6849,6850,6851,6852,6853,6854,6855,6856,6857,6858,6859,6860,6861,6862,6863,6864,6865,6866,6867,6868,6869,6870,6871,6872,6873,6874,6875,6876,6877,6878,6879,6880,6881,6882,6883,6884,6885,6886,6887,6888,6889,6890,6891,6843,6892,6893,6894,6895,6896,6897,6898,6899,6900,6901,6902,6903,6904,6905,6906,6907,6908,6909,6931,6910,6911,6912,6913,6914,6915,6916,6917,6918,6919,6838,6839,6840,6841,6920,6842,6921,6922,6923,6924,6925,6926,6927,6928,6929,6930", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 6159, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 6110, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 6156, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 6078, }, }, }, }, }, 2024 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 6802, }, NINETYDAYS => { P4PPROGRAMID => "6786,6788,6739", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 6728, }, NINETYDAYS => { P4PPROGRAMID => "6756,6758,6783", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 6777, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 6733, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 6730, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 6729, }, }, }, }, }, 2025 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 7566, }, NINETYDAYS => { P4PPROGRAMID => "7739,7740,7741", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 7576, }, NINETYDAYS => { P4PPROGRAMID => "7745,7746,7747", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 7598, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 7595, }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 7569, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 7568, }, }, }, }, }, }; use constant _MVP_PROGRAM_DATA => { 2023 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "6543,6590,6589", }, NINETYDAYS => { P4PPROGRAMID => "6548,6549,6550,6551,6570,6572,6575,6576,6571,6573,6574,6577", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "6542,6587,6588", }, NINETYDAYS => { P4PPROGRAMID => "6544,6545,6546,6547,6578,6579,6582,6569,6591,6580,6581,6568", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "6559,6562,6561" }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "6558,6563,6560" }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "6541,6567,6586", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "6539,6566,6585", }, }, }, }, }, 2024 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "6772,7464,7471,7478", }, NINETYDAYS => { P4PPROGRAMID => "6773,6774,6776", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "6763,7459,7467,7475", }, NINETYDAYS => { P4PPROGRAMID => "6765,6767,6769", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "6796,7399,7401,7403" }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "6794,7398,7400,7402" }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "6798,7421,7423,7441", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "6797,7420,7422,7440", }, }, }, }, }, 2025 => { PI => { POSSIBLESCORE => 100, DISPLAYTEXT => 'Promoting Interoperability (PI)', DISPLAYORDER => 2, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "7579,7582,7586,7590", }, NINETYDAYS => { P4PPROGRAMID => "7748,7749,7719,7750,7720,7718,7721,7722", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "7567,7581,7585,7589", }, NINETYDAYS => { P4PPROGRAMID => "7755,7752,7754,7756,7751,7753,7757,7758", }, }, }, }, IA => { POSSIBLESCORE => 40, DISPLAYTEXT => 'Improvement Activities (IA)', DISPLAYORDER => 3, ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "7630,7621,7624,7628" }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "7627,7620,7626,7623" }, }, }, }, Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => "7602,7584,7573,7605", }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => "7601,7577,7572,7604", }, }, }, }, }, }; use constant _MIPS_QUALITY_FOR_QMASS_PROGRAM_DATA => { Quality => { POSSIBLESCORE => 60, DISPLAYTEXT => 'Quality', DISPLAYORDER => 1, SUBMISSONMETHODS => [qw( EHR REGISTRY )], ENTITYTYPE => { GROUP => { FULLYEAR => { P4PPROGRAMID => 4497, }, }, INDIVIDUAL => { FULLYEAR => { P4PPROGRAMID => 4496, }, }, }, }, }; # Both ACI and PI has to co exsist here as they are exclusively part or 2017 and 2018 program use constant MIPS_CATEGORIES => { Quality => 1, ACI => 1, IA => 1, PI => 1, }; use constant DIMENSIONS => [ { FILTERKEY => 'year', DISPLAYNAME => 'Year', OPTIONS => sub { my ($dbh) = @_; return [ map { { OptionName => $_, OptionID => $_, } } @{ GetMIPSProgramYearList($dbh) } ]; } }, { FILTERKEY => 'category', DISPLAYNAME => 'Category', OPTIONS => sub { my ($dbh) = @_; my @program_ids_by_category_year; foreach my $year (@{ GetMIPSProgramYearList($dbh) }) { my %program_ids_by_category = (); foreach my $category (@{ _SupportedCategoriesForYear({ YEAR => $year, }) }) { my $program_ids = Athena::P4P::App::PerformanceData::GetMIPSProgramList($dbh, { CATEGORY => $category, YEARS => [$year], }); if (! defined $program_ids_by_category{$category}) { $program_ids_by_category{$category} = $program_ids; } else { push @{ $program_ids_by_category{$category} }, @$program_ids; } } push @program_ids_by_category_year, map { { Year => $year, OptionName => $_, OptionID => [ sort { $a <=> $b } @{ $program_ids_by_category{$_} } ], } } keys %program_ids_by_category; } return \@program_ids_by_category_year; }, }, { FILTERKEY => 'provider.npi', DISPLAYNAME => 'Provider', OPTIONS => sub { my ($dbh) = @_; return [ map { { OptionName => $_->{PROVIDERNAME}, OptionID => $_->{NPI}, } } @{ Athena::P4P::App::PerformanceData::GetProviderDimensionValues($dbh) || [] } ]; }, }, { FILTERKEY => 'provider.federalidnumber', DISPLAYNAME => 'TIN', OPTIONS => sub { my ($dbh) = @_; return [ map { { OptionName => $_->{NAME}, OptionID => $_->{FEDERALIDNUMBER}, } } @{ Athena::P4P::App::PerformanceData::GetFederalIDNumberDimensionValues($dbh) || [] } ]; }, }, ]; use constant DEFAULT_MAX_POINTS => 10; use constant MAX_ROWS_TO_RETURN => 200; use constant DEFAULT_ORDER_BY => [ { SortKey => 'MEASUREPOINTS', Direction => 'ASC', }, ]; # Both ACI and PI has to co exsist here as they are exclusively part or 2017 and 2018 program use constant SUBMISSION_METHODS_FOR_PROGRAM => { ACI => [], IA => [], PI => [], Quality => [qw( EHR REGISTRY )], }; # GetMIPSPerformanceData accepts a list of keys to order by; this hashref # maps each key to the columns of p4pmeasureperformance that we should use # order by when passed that key. Keys that correspond directly to columns # of p4pmeasureperformance are not included here. See the block comment for # GetMIPSPerformanceData for more details on sorting. use constant SORT_KEYS_TO_COLUMNS => { PROVIDERNAME => ['PROVIDERPLCLASTNAME', 'PROVIDERPLCFIRSTNAME'], MEASUREPOINTS => ['WEIGHTEDMEASUREPOINTS'], RELATIVEPERFORMANCE => ['RELATIVEPERFORMANCE'], ROUNDEDPERCENTAGE => ['ROUNDEDPERFORMANCERATE'], SORTEDNUMERATOR => ['SORTEDNUMERATOR'], }; use constant UNIQUE_MEASURE_PERFORMANCE_COLUMNS => [qw( P4PPROGRAMID CRITERIACODE NPI PROVIDERID FEDERALIDNUMBER )]; use constant P4PMEASUREPERFORMANCE_COLUMNS => [qw( PROVIDERID FEDERALIDNUMBER P4PPROGRAMID P4PMEASUREID P4PMEASURESUBSCRIPTIONID NPI CATEGORY MEASURENAME CRITERIACODE PROVIDERPLCFIRSTNAME PROVIDERPLCLASTNAME DENOMINATOR NUMERATOR EXCLUDED PERFORMANCERATE ROUNDEDPERFORMANCERATE WEIGHTEDMEASUREPOINTS MAXPOINTS DISPLAYPERFORMANCERATE ISINVERSEMEASURE PROGRAMKEY STATUS SUBGROUPID )]; use Athena::P4P::App::ProgramDefinition; use Athena::RolloutToggle; use Athena::ServiceBus::Dispatcher; use Athena::Util::Assert; use Athena::Util::Hash; use Athena::Util::List; use Athena::Util::Text; use Athena::Util::SQL qw( ProcessTable ); use Athena::Util::Hash qw( HashGroupBy ); use AthenaSecurity; use Clinical::P4P::Attestation; use Clinical::P4P::Blacklist; use Clinical::P4P::Persistence::MultiProviderSubmission; use Clinical::P4P::Persistence::ProgramSubmission; use Clinical::P4P::ReportingPeriod; use Clinical::P4P::Submission::MIPSACIModifiedStage2; use Clinical::P4P::Submission::MIPSQuality; use Clinical::P4P::Submission::Utils::MIPSSnapshotAssessment; use Athena::P4P::Persistence::ProgramEligibility; use Clinical::P4P::Utils; use Clinical::P4P::Utils::Practice; use Clinical::P4P::Submission; use Global; use List::AllUtils; use List::Util; use List::Util qw(max min); use SQL::Select; use SQL; ################################################################################ # GetDimensions # # Description: # Get a list of supported dimensions. # # Return Value: # Reference to a list of hashrefs with the following keys: # # DisplayName - String, the name of the dimension to be displayed in the UI. # FilterKey - String representing the dimension. ################################################################################ sub GetDimensions { return [ map { { DisplayName => $_->{DISPLAYNAME}, FilterKey => $_->{FILTERKEY}, } } @{ DIMENSIONS() } ]; } ############################################################################### # GetDimensionValues # # Description: # Retrieve the option values for the various filters supported by the MIPS # dashboard. # # Parameters: # Required: # FILTERKEY String # # Returns: # Dies if passed an unrecognized FILTERKEY, otherwise returns a listref of # hashrefs each with the following keys: # FilterKey # OptionName # OptionID ############################################################################### sub GetDimensionValues { my ($dbh, $args) = @_; my $filter_key = $args->{FILTERKEY}; my ($dimension) = grep { $filter_key eq $_->{FILTERKEY} } @{ DIMENSIONS() }; unless ($dimension) { die "$filter_key is an unrecognized filter type"; } my $options_spec = $dimension->{OPTIONS}; # The OPTIONS key may be a subroutine reference or a value. # Return the result of the subroutine or the value as # appropriate. my $options = (ref $options_spec eq 'CODE') ? $options_spec->($dbh) : $options_spec; return [ map { { FilterKey => $filter_key, %{ $_ }, } } @$options ]; } ############################################################################### # GetMIPSProgramYearList # # Description: # Retrieve the valid MIPS program years supported by the MIPS dashboard. # # Parameters: # Required: # None. # Optional: # None. # # Returns: # An arrayref of program years (e.g., [2017, 2018]). ############################################################################### sub GetMIPSProgramYearList { my ($dbh, $is_mvp) = @_; # TODO: Use a query to get the program years. my @program_years; my $render_2025_dropdown = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'CLQI_6580_2025_YOY_ROLLOVER', USERNAME => $Global::session{USERNAME}, }) eq 'ON'; if ($render_2025_dropdown) { if($is_mvp) { @program_years = reverse sort { $a <=> $b } keys %{ _MVP_PROGRAM_DATA() }; } else { @program_years = reverse sort { $a <=> $b } keys %{ _MIPS_PROGRAM_DATA() }; } } else { my %programdata = %{_MIPS_PROGRAM_DATA()}; delete $programdata{'2025'}; @program_years = reverse sort { $a <=> $b } keys %programdata; } return \@program_years; } ############################################################################### # GetProviderDimensionValues # # Description: # Gets values for the Provider filter for the MIPS dashboard. # # Parameters: # Required: # None. # Optional: # YEARS ArrayRef A list of MIPS program years. Defaults to [2017]. # ENTITYTYPES ArrayRef A list of entity types: INDIVIDUAL, GROUP, or both. # Defaults to ['INDIVIDUAL']. # # Returns: # Listref of hashrefs each with the following keys # NPI # PROVIDERNAME ############################################################################### sub GetProviderDimensionValues { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( )], [qw( YEARS ENTITYTYPES )], ); my $entity_types = $args->{ENTITYTYPES} // ['INDIVIDUAL']; my $providersql = Clinical::P4P::Persistence::MultiProviderSubmission->ProvidersWithDenormalizedDataSQL({ # Should be the same list of providers in all three programs. P4PPROGRAMIDS => GetMIPSProgramList($dbh, { ENTITYTYPES => $entity_types, }), INCLUDEFEDERALIDNUMBERS => 1, }); my $npicategoryid = BusCall::Provider::GetProviderNumberCategoryID($dbh, { KEY => 'NPI', }); my @dimensionvalues = SQL::Select->new( )->SelectAndGroupBy( "provider.id providerid", "providerswithdata.federalidnumber", "providernumber.providernumber npi", "provider.plclastname || ', ' || provider.plcfirstname providername", )->From( ["(??) providerswithdata", $providersql], "provider", "providernumber", )->Joins( "providerswithdata.providerid = provider.id", "providernumber.providerid = provider.id", )->Where( "provider.deleted is null", "providernumber.deleted is null", ["providernumber.providernumbercategoryid = ?", $npicategoryid], )->TableHash($dbh); my $isenabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_REPRESENTATIVE_PROVIDER_LOGIC_UPDATE', }) eq 'ON'; if ($isenabled) { @dimensionvalues = @{Athena::P4P::App::PerformanceData::GetRepresentativeProviderNameBasedClinicalEncouterY($dbh,{ DATAS => \@dimensionvalues, })}; } # Add option for all providers unshift @dimensionvalues, { NPI => 'TINONLY', PROVIDERNAME => 'All', }; return \@dimensionvalues; } ################################################################################ # GetMIPSCategoryDataModels # # Description: # Builds data models for MIPS category scores shown in the MIPS dashboard. # # Parameters: # YEARS - ArrayRef of Program years # CATEGORYSCORES (optional) - ArrayRef of category score data. # # Returns: # Ordered ArrayRef of HashRefs containing # CATEGORY (String) # DISPLAYTEXT (String) # DISPLAYORDER (Integer) # P4PPROGRAMID (Integer) # POSSIBLESCORE (Integer) # SCORE (optional) (Number) # FINALSCORE (optional) (Number) # SCALEDSCORE (optional) (Number) # REPORTINGPERIOD (optional) (string) ################################################################################ sub GetMIPSCategoryDataModels { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( YEARS )], [qw( CATEGORYSCORES FEDERALIDNUMBER P4PPROGRAMIDS PROVIDERID )], ); my $years = $args->{YEARS}; my $category_scores = $args->{CATEGORYSCORES}; # For now we only support a single year at a time. We may need to figure # this out once we decide how we are handling multiple-years on the front-end. my $year = $years->[0]; # Build array of category data for given year my $program_year_data = _MIPS_PROGRAM_DATA()->{$year}; my @category_data = map { { POSSIBLESCORE => $program_year_data->{$_}->{POSSIBLESCORE}, DISPLAYTEXT => $program_year_data->{$_}->{DISPLAYTEXT}, DISPLAYORDER => $program_year_data->{$_}->{DISPLAYORDER}, CATEGORY => $_, SCORE => undef, SCALEDSCORE => undef, SCOREPREVIEWATHENA => undef, P4PPROGRAMID => undef, REPORTINGPERIOD => undef, }; } keys %$program_year_data; # Build a map for easier lookup my %score_data = map { $_->{CATEGORY} => $_, } @$category_scores; # Merge scores into category data my $enabledversion = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_DISPLAY_MAX_SCORES_FROM_CMS', }) eq 'ON'; if($enabledversion) { @category_data = map { my $scores = $score_data{$_->{CATEGORY}} // {}; my $model = { %{ $_ }, SCORE => $scores->{SCORE}, FINALSCORE => $scores->{FINALSCORE}, SCALEDSCORE => $scores->{SCALEDSCORE}, SCOREPREVIEWATHENA => $scores->{SCOREPREVIEWATHENA}, P4PPROGRAMID => $scores->{P4PPROGRAMID}, }; my $possiblescore; if ($scores->{WEIGHT} ne undef) { $possiblescore = $scores->{WEIGHT}; } elsif($_->{CATEGORY} eq 'IA') { $possiblescore = 15; }elsif($_->{CATEGORY} eq 'Quality' ) { if($year >= 2022) { $possiblescore = 30; } elsif($year >= 2021) { $possiblescore = 40; } else { $possiblescore = 45; } } else { $possiblescore = 25; } $model->{POSSIBLESCORE} = $possiblescore; $model; } @category_data; } else { @category_data = map { my $scores = $score_data{$_->{CATEGORY}} // {}; my $model = { %{ $_ }, SCORE => $scores->{SCORE}, FINALSCORE => $scores->{FINALSCORE}, SCALEDSCORE => $scores->{SCALEDSCORE}, SCOREPREVIEWATHENA => $scores->{SCOREPREVIEWATHENA}, P4PPROGRAMID => $scores->{P4PPROGRAMID}, }; $model; } @category_data; } # CLQI-4516 from 2022 program years, Display Reporting Period for MIPS PI if ($year >= 2022) { foreach my $categorydata (@category_data) { if ($categorydata->{CATEGORY} eq 'PI') { # Use Enrolled PI program to fetch Reporting period, if Category score detail not # have program id. if ($categorydata->{P4PPROGRAMID} eq '') { my $enrolled_program_list = Athena::P4P::App::PerformanceData::GetEnrolledPrograms($dbh, { P4PPROGRAMIDS => $args->{P4PPROGRAMIDS}, PROVIDERID => $args->{PROVIDERID}, FEDERALIDNUMBER =>$args->{FEDERALIDNUMBER}, }); foreach my $enrolledprogram (@{$enrolled_program_list}) { if(Athena::P4P::App::PerformanceData::GetMIPSCategory($dbh, $enrolledprogram->{P4PPROGRAMID}) eq 'PI') { $categorydata->{P4PPROGRAMID} = $enrolledprogram->{P4PPROGRAMID}; last; } } } if ($categorydata->{P4PPROGRAMID} ne '') { my $reporting_period = Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => $categorydata->{P4PPROGRAMID}, }); my $startdate = AthenaDate::FormatDate($reporting_period->{PROGRAMSTART}, 'mm-dd-yyyy'); my $enddate = AthenaDate::FormatDate($reporting_period->{PROGRAMEND}, 'mm-dd-yyyy'); $categorydata->{REPORTINGPERIOD} = " : $startdate to $enddate"; } $categorydata->{PIREQUIREMENT} = ''; if ($categorydata->{P4PPROGRAMID} ne '') { my $registry_requirement_not_met = _GetPHRStatusForPIRequirementAlert($dbh, { FEDERALIDNUMBER => $args->{FEDERALIDNUMBER}, P4PPROGRAMID => $categorydata->{P4PPROGRAMID}, PROVIDERID => $args->{PROVIDERID}, }); $categorydata->{PIREQUIREMENT} = "UNMET" if ($registry_requirement_not_met); } } } } return \@category_data; } ############################################################################### # _GetPHRStatusForPIRequirementAlert # # Description: # Checks whether Immunization Registry and Electronic Case Registry has been # satisfied or not for a given entity, based on that, PI Requirement UnMet # popup will be displayed in MIPS dashboard. # # Parameters: # Required: # FEDERALIDNUMBER - TIN of the provider for whom to request details. # P4PPROGRAMID - P4P Program ID. # Optional: # PROVIDERID - ID of provider to request details for. # # Returns: # Boolean: 0 or 1. ############################################################################### sub _GetPHRStatusForPIRequirementAlert { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( FEDERALIDNUMBER P4PPROGRAMID )], [qw( PROVIDERID )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $provider_id = $args->{PROVIDERID}; my @phrregistries = ('Immunization Registry', 'Electronic Case Registry Reporting'); my $phrregistrysatisfactionstatussql = SQL::Select->new( )->Select( "p4pmeasureperformance.measurename", "p4pmeasureperformance.status", )->From( "p4pmeasureperformance", )->Where( "p4pmeasureperformance.deleted is null", ["p4pmeasureperformance.federalidnumber = ?", $federal_id_number], ["p4pmeasureperformance.p4pprogramid = ?", $p4p_program_id], ["p4pmeasureperformance.measurename in (??)", \@phrregistries] ); if ($provider_id) { $phrregistrysatisfactionstatussql->Where( ["p4pmeasureperformance.providerid = ?", $provider_id], ); } my @phrresgistrysatisfactionstatus = $phrregistrysatisfactionstatussql->TableHash($dbh); my %registrysatisfactionstatus; foreach my $measure (@phrresgistrysatisfactionstatus) { $registrysatisfactionstatus{$measure->{MEASURENAME}} = $measure->{STATUS}; } if (($registrysatisfactionstatus{'Immunization Registry'} eq 'Satisfied' || $registrysatisfactionstatus{'Immunization Registry'} eq 'Excluded') && ($registrysatisfactionstatus{'Electronic Case Registry Reporting'} eq 'Satisfied' || $registrysatisfactionstatus{'Electronic Case Registry Reporting'} eq 'Excluded') ) { return 0; } return 1; } ############################################################################### # GetReportCardDetails # # Description: # Gets performance details for the MIPS dashboard based on the selected provider/TIN. # # Parameters: # Required: # FEDERALIDNUMBER - TIN of the provider for whom to request details. # YEARS - ArrayRef of MIPS program years. # Optional: # PROVIDERID - ID of provider to request details for. # # Returns: # HashRef containing the following keys: # # PROVIDERNAME - String - the name of the provider, per the Provider Subsystem # or # MEDICALGROUPNAME - String - name of the medical group. # # COMPOSITEPERFORMANCESCORE - Integer - the CPS of the provider, per CMS # FINALCOMPOSITEPERFORMANCESCORE - Integer - the final, post submission, # CPS of the provider, per CMS. # # CATEGORYSCORES - Arrayref of hashrefs - score data for each category. # CATEGORY (String) # DISPLAYTEXT (String) # DISPLAYORDER (Integer) # P4PPROGRAMID (Integer) # POSSIBLESCORE (Integer) # SCORE (optional) (Number) # FINALSCORE (optional) (Number) - final score for the category after submission. # SCALEDSCORE (optional) (Number) ############################################################################### sub GetReportCardDetails { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( FEDERALIDNUMBER YEARS )], [qw( PROVIDERID )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $years = $args->{YEARS}; my $entity_types = $args->{PROVIDERID} ? ['INDIVIDUAL'] : ['GROUP']; my $provider_name; my $medical_group_name; if ( $provider_id ) { # Pull the provider name from the Provider subsystem my $isenabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_REPRESENTATIVE_PROVIDER_LOGIC_UPDATE', }) eq 'ON'; if ($isenabled) { $provider_name = Clinical::P4P::Utils::GetMIPSProviderName($dbh, { PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, YEAR => $years->[0], }); } else { $provider_name = BusCall::Provider::GetProviderName($dbh, { PROVIDERID => $provider_id, }); } } else { $medical_group_name = Clinical::P4P::Utils::Practice::GetMedicalGroupNameByTIN($dbh, { FEDERALIDNUMBER => $federal_id_number, }); } my $p4p_program_ids = GetMIPSProgramList($dbh, { YEARS => $years, ENTITYTYPES => $entity_types, }); # Pull the composite performance score from CMS my @performance_score_data = @{Clinical::P4P::Persistence::ProgramSubmission::GetPerformanceScores($dbh, { # In the future, we may want to pass this list of program ids through from the front # end, once the MIPS Dashboard will need to represent 2017 or 2018 P4PPROGRAMIDS => $p4p_program_ids, # Limit to just this provider/TIN FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, })}; my $composite_performance_score; my $final_composite_performance_score; my $score_updated_date; my $category_score_list; my $error_msg; my $bonus_points; my $max_total_score; my $score_preview_athena; if (scalar @performance_score_data > 0) { $composite_performance_score = $performance_score_data[0]->{COMPOSITESCORE}; $final_composite_performance_score = $performance_score_data[0]->{FINALCOMPOSITESCORE}; $score_updated_date = $performance_score_data[0]->{SCOREDATETIME}; $category_score_list = $performance_score_data[0]->{CATEGORYSCORES}; $error_msg = $performance_score_data[0]->{ERRORMESSAGE}; $bonus_points = $performance_score_data[0]->{BONUSPOINTS}; $max_total_score = $performance_score_data[0]->{MAXTOTALSCORE}; $score_preview_athena = $performance_score_data[0]->{SCOREPREVIEWATHENA}; } my $category_data = GetMIPSCategoryDataModels($dbh,{ YEARS => $years, CATEGORYSCORES => $category_score_list, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMIDS => $p4p_program_ids }); my $show_mips_internal_scoring_legend = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_INTERNAL_SCORING', USERNAME => $Global::session{USERNAME}, }) eq 'ON'; my $shouldshowqrdaimportmessage = _GetMIPSMessageForQRDA1Import($dbh,{ FEDERALIDNUMBER => $federal_id_number, YEARS => $years, PROVIDERID => $provider_id, }); my @results; my $show_mips_eligibility_info = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'ELIGIBILITY_INFO_MIPS_DASHBOARD', }) eq 'ON'; if($show_mips_eligibility_info) { my $eligibilitystatussql = Athena::P4P::Persistence::ProgramEligibility::GetMIPSEligibilityStatus($dbh,{ FEDERALIDNUMBER => $federal_id_number, YEAR => $years->[0], PROVIDERID => $provider_id, }); @results = $eligibilitystatussql->TableHash($dbh); } my $is_small_practice_enabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'CLQI_6298_SMALL_PRACTICE_MIPS', }) eq 'ON'; my $small_practicioner_value; if($is_small_practice_enabled && (scalar @performance_score_data > 0) && $years->[0] >= 2024) { $small_practicioner_value = $performance_score_data[0]->{ISSMALLPRACTICEELIGIBLE} ne undef && $performance_score_data[0]->{ISSMALLPRACTICEELIGIBLE} eq '1' ? 'Y' : 'N'; } my $show_tinor_subid_mipsdb = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_DB_TINOR_SUBID_DPLY', }) eq 'ON'; my $tinoverrideapplicable = 'false'; my $tinoverride; my $qppsubmissionid; if($show_tinor_subid_mipsdb) { my $reporting_period = Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => @$p4p_program_ids[0], }); $tinoverride = Athena::P4P::Persistence::QPP::GetTINOverride($dbh, { PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, EFFECTIVEDATE => $reporting_period->SubmissionEnd(), }); $tinoverrideapplicable = $tinoverride == $federal_id_number ? 'false' : 'true'; my $program_submission = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => @$p4p_program_ids[0], PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); # The submission number (while often 1) is used for things below my $submission_number = $program_submission->CurrentSubmissionNumber() || 1; my @qpp_submissions = Athena::P4P::Persistence::QPP::GetQPPSubmissions($dbh, { PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, PROGRAMYEAR => $reporting_period->ProgramYear(), SUBMISSIONNUMBER => $submission_number, REQUESTTYPE => 'FINALSCORING', STATUS => 'SUCCESS', }); if($qpp_submissions[0] != undef) { $qppsubmissionid = $qpp_submissions[0]->{QPPSUBMISSIONID}; } } #submission status my $submission_status = undef; my $programinfo; my $currentdate = AthenaDate::AthenaToday(); my $submissionwindowended = 0; my $submission_status_substate = undef; my $programs = Clinical::P4P::Utils::GetProgramInfo($dbh); if ( ($programs->{@$p4p_program_ids[0]})) { $programinfo = $programs->{@$p4p_program_ids[0]}; $submissionwindowended = AthenaDate::Date1IsLaterThanDate2($currentdate, AthenaDate::AddDays($programinfo->{REPORTINGPERIODEND} , $programinfo->{SUBMISSIONWINDOW})); } $submission_status_substate = GetSubmissionStatus($dbh, { P4PPROGRAMID => @$p4p_program_ids[0], FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, }); $submission_status = 'Processing'; # if qpp id is available it means submission is successful # qpp id can be there for Unknown status as we submit only Quality EHR my $isallcategorysubmitted = IsAllMIPSCategerorySubmitted($dbh, { P4PPROGRAMID => @$p4p_program_ids[0], FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, }); if($qppsubmissionid && $isallcategorysubmitted) { #check if all categories are submitted if($isallcategorysubmitted) { $submission_status = 'Submitted'; } } if(!$isallcategorysubmitted) { if(!$submissionwindowended) { if($submission_status_substate eq 'UNKNOWN_ELIGIBILITY_STATUS' || $submission_status_substate eq 'NOT_OPTED_IN_ELIGIBILITY_STATUS' || $submission_status_substate eq 'COMPOSITE_SCORE_THRESHOLD' || $submission_status_substate eq 'NPI_TIN_MISMATCH_ELIGIBILITY_STATUS' || $submission_status_substate eq 'INELIGIBLE_ELIGIBILITY_STATUS') { $submission_status = 'Pending Action'; } } #outside submission window else{ if($submission_status_substate eq 'UNKNOWN_ELIGIBILITY_STATUS' || $submission_status_substate eq 'NOT_OPTED_IN_ELIGIBILITY_STATUS' || $submission_status_substate eq 'NPI_TIN_MISMATCH_ELIGIBILITY_STATUS' || $submission_status_substate eq 'INELIGIBLE_ELIGIBILITY_STATUS') { $submission_status = 'Requirement unmet'; } elsif($submission_status_substate eq 'COMPOSITE_SCORE_THRESHOLD') { $submission_status = 'Not Submitted'; } } } if($years->[0] >= 2023 && $qppsubmissionid && $error_msg ne undef && scalar @$error_msg > 0){ $submission_status = 'Submission Failed'; } return { ( $provider_name ? ( PROVIDERNAME => $provider_name ) : () ), ( $medical_group_name ? ( MEDICALGROUPNAME => $medical_group_name ) : () ), COMPOSITEPERFORMANCESCORE => $composite_performance_score, FINALCOMPOSITEPERFORMANCESCORE => $final_composite_performance_score, # DateTime String in iso8601 SCORELASTUPDATED => $score_updated_date, # Send back an array so that the front end is certain an array exists CATEGORYSCORES => $category_data, SHOULDSHOWQRDAIMPORTMESSAGE => ($shouldshowqrdaimportmessage)?'true':'false', SHOULDSHOWSMALLPRACTICEMESSAGE => ($is_small_practice_enabled) && $small_practicioner_value eq 'Y' ? 'true' : 'false', ERRORMESSAGE => $error_msg, BONUSPOINTS => $bonus_points ? $bonus_points : undef, MAXTOTALSCORE => $max_total_score, SCOREPREVIEWATHENA => $score_preview_athena, ELIGIBILITY => $results[0]->{MIPSELIGIBILITYSTATUS}, ELIGIBILITYSTATUSOVERRIDDEN => $results[0]->{ELIGIBILITYSTATUSOVERRIDDEN}, YEAR => $years->[0], SHOWMIPSINTERNALSCORINGLEGEND => ( $show_mips_internal_scoring_legend ) ? 'true' : 'false', SHOWTINOVERRIDEMESSAGE => $tinoverrideapplicable, TINOVERRIDE => $tinoverride, QPPSUBMISSIONID => $qppsubmissionid, SUBMISSIONSTATUS => $submission_status, SUBMISSIONSTATUSSUBSTATE => $submission_status_substate, SHOWQPPSCORESTATUSMESSAGE => _CheckFinalScoringStatus({FINALCOMPOSITEPERFORMANCESCORE => $final_composite_performance_score, CATEGORYSCORES => $category_data, QPPSUBMISSIONID => $qppsubmissionid, SUBMISSIONSTATUS => $submission_status, PROGRAMYEAR => $years->[0]}) }; } ############################################################################### # GetSmallPracticeData # # Description: # Gets the small practice details for the MIPS dashboard/ QPP Scoring based on the selected provider/TIN. # # Parameters: # Required: # FEDERALIDNUMBER - TIN of the provider for whom to request details. # YEARS - ArrayRef of MIPS program years. # Optional: # PROVIDERID - ID of provider to request details for. # # Returns: # HashRef containing the following keys: # # boolean flag ############################################################################### sub GetSmallPracticeData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( FEDERALIDNUMBER YEAR P4PPROGRAMID )], [qw( PROVIDERID )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $year = $args->{YEAR}; my $p4pprogramid = $args->{P4PPROGRAMID}; my $eligibilitystatussql = Athena::P4P::Persistence::ProgramEligibility::GetSmallPracticeTableData($dbh,{ FEDERALIDNUMBER => $federal_id_number, YEAR => $year, PROVIDERID => $provider_id, }); my @small_practicioner_value = $eligibilitystatussql->TableHash($dbh); if (scalar @small_practicioner_value < 1) { return 'N'; } if ($provider_id eq undef) { my @claimbasedproviders = @{Clinical::P4P::Utils::GetClaimBasedLogic($dbh, {P4PPROGRAMID => $p4pprogramid})->{$federal_id_number} || []}; if (scalar @claimbasedproviders > 15 || scalar @claimbasedproviders == 0) { return 'N'; } else { foreach my $claimbasedprovider(@claimbasedproviders) { if (scalar (grep {($_->{GROUPSMALLGROUPPRACTITIONERYN} eq undef || $_->{GROUPSMALLGROUPPRACTITIONERYN} eq 'N') && $_->{PROVIDERID} eq $claimbasedprovider} @small_practicioner_value) > 0 ) { return 'N'; } } } return 'Y'; } else { return $small_practicioner_value[0]->{INDIVSMALLGROUPPRACTITIONERYN} eq undef ? 'N' : $small_practicioner_value[0]->{INDIVSMALLGROUPPRACTITIONERYN}; } } sub _CheckFinalScoringStatus { my ($args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( )], [qw( PROGRAMYEAR FINALCOMPOSITEPERFORMANCESCORE QPPSUBMISSIONID SUBMISSIONSTATUS CATEGORYSCORES )], ); my $final_composite_performance_score = $args->{FINALCOMPOSITEPERFORMANCESCORE}; my $category_data = $args->{CATEGORYSCORES}; my $year = $args->{PROGRAMYEAR}; my $qppsubmissionid = $args->{QPPSUBMISSIONID}; my $submission_status = $args->{SUBMISSIONSTATUS}; my $showbanner = 'false'; my $finalscore = 'false'; if($category_data ne undef){ foreach my $category (@$category_data) { if($category->{FINALSCORE} ne undef){ $finalscore = 'true'; } } } if($submission_status eq 'Submission Failed'){ return 'true'; } if($year >= 2023 && $qppsubmissionid ne undef && $final_composite_performance_score eq undef && $finalscore eq 'true'){ return 'true'; } return $showbanner; } ############################################################################### # GetFederalIDNumberDimensionValues # # Description: # Retrieve the values for the TIN filter for the MIPS dashbaord. # # Parameters: # Required: # None. # Optional: # YEARS ArrayRef A list of MIPS program years. Defaults to [2017]. # ENTITYTYPES ArrayRef A list of entity types: INDIVIDUAL, GROUP, or both. # Defaults to ['INDIVIDUAL']. # Returns: # Listref of hashrefs each with the following keys # FEDERALIDNUMBER # NAME ############################################################################### sub GetFederalIDNumberDimensionValues { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( )], [qw( YEARS ENTITYTYPES )], ); my $entity_types = $args->{ENTITYTYPES} // ['INDIVIDUAL']; my $providersql = Clinical::P4P::Persistence::MultiProviderSubmission->ProvidersWithDenormalizedDataSQL({ INCLUDEFEDERALIDNUMBERS => 1, # Should be the same list of providers in all three programs. P4PPROGRAMIDS => GetMIPSProgramList($dbh, { ENTITYTYPES => $entity_types, }), }); my @dimensionvalues = SQL::Select->new( )->SelectAndGroupBy( "providerswithdata.federalidnumber", "providerswithdata.federalidnumber as name", )->From( ["(??) providerswithdata", $providersql], )->TableHash($dbh); return \@dimensionvalues; } ############################################################################### # GetMIPSPerformanceData # # Description: # Retrieves measure-level aggregated performance data for the MIPS program. # # Parameters: # Required: # None. # Optional: # ORDERBY Arrayref Sort the results by the following keys. Keys can either be # directly column names in p4pmeasureperformance, or keys of # SORT_KEYS_TO_COLUMNS() which maps keys to column names in # p4pmeasureperformance. # FEDERALIDNUMBERS Arrayref Only retrieve data related to these TINs # NPIS Arrayref Only retrieve data for providers with these NPIs # P4PPROGRAMIDS Arrayref Only retrieve data related to these programs. # NOROWLIMIT Boolean Don't limit the returned results to MAX_ROWS_TO_RETURN() rows # YEARS Arrayref The MIPS program years that apply. Defaults to [2017]. # ENTITYTYPES Arrayref A list containing INDIVIDUAL, GROUP, or both. Defaults to ['INDIVIDUAL']. # # Returns: # Listref of hashrefs each with the following keys # CATEGORY # DENOMINATOR # EXCLUDED # FEDERALIDNUMBER # MEASURENAME # NUMERATOR # PERFORMANCERATE # PROVIDERID # PROVIDERNAME ############################################################################### sub GetMIPSPerformanceData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( )], [qw( FEDERALIDNUMBERS NOROWLIMIT NPIS ORDERBY P4PPROGRAMIDS YEARS ENTITYTYPES )], ); my $no_row_limit = $args->{NOROWLIMIT}; my $years = $args->{YEARS} // [2017]; my $entity_types = $args->{ENTITYTYPES} // ['INDIVIDUAL', 'GROUP']; my $federal_id_numbers = $args->{FEDERALIDNUMBERS}; # Get programids for current filters my $filtered_program_ids = GetMIPSProgramList($dbh, { YEARS => $years, ENTITYTYPES => $entity_types, FEDERALIDNUMBERS => $federal_id_numbers, }); # If program ids were passed in, we want to filter them based on our list of # program ids already filtered by other args (years, entity types, tins) if($args->{P4PPROGRAMIDS}) { $filtered_program_ids = [ Athena::Util::List::GetIntersection( $args->{P4PPROGRAMIDS}, $filtered_program_ids ) ]; } my $sql_args = Athena::Util::Hash::HashSlice($args, [qw( FEDERALIDNUMBERS NPIS ORDERBY )] ); $sql_args->{P4PPROGRAMIDS} = $filtered_program_ids; my $sql = _MIPSPerformanceDataSQL($dbh, $sql_args); if (!$no_row_limit) { $sql = SQL::Select->Limit($sql, MAX_ROWS_TO_RETURN()); } my @data = $sql->TableHash($dbh); my $resultdata; my $isenabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_REPRESENTATIVE_PROVIDER_LOGIC_UPDATE', }) eq 'ON'; if ($isenabled && scalar @data > 0) { $resultdata = Athena::P4P::App::PerformanceData::GetRepresentativeProviderNameBasedClinicalEncouterY($dbh, { DATAS => \@data, YEAR => $years->[0], }); return $resultdata; } else { return \@data; } } ############################################################################### # GetMIPSPerformanceDataWithRowCount # # Description: # Retrieves measure-level aggregated performance data for the MIPS program along # with a boolean indicating whether or not more rows exist. # # Parameters: # Required: # None. # Optional: # ORDERBY Listref Sort the results by the following columns # FEDERALIDNUMBERS Listref Only retrieve data related to these TINs # NPIS Listref Only retrieve data for providers with these NPIs # P4PPROGRAMIDS Listref Only retrieve data related to these programs. # YEARS Listref Only retrieve data related to these program years. # # Returns: # Hashref with the following keys # DATA Listref See GetMIPSPerformanceData # HASMORERESULTS Boolean ############################################################################### sub GetMIPSPerformanceDataWithRowCount { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( )], [qw( FEDERALIDNUMBERS NPIS ORDERBY P4PPROGRAMIDS YEARS )], ); my $p4p_program_ids = $args->{P4PPROGRAMIDS}; my $years = $args->{YEARS}; my $federal_id_numbers = $args->{FEDERALIDNUMBERS}; my $npis = $args->{NPIS}; my @results = @{ GetMIPSPerformanceData($dbh, $args) || [] }; my $row_count; if (@results >= MAX_ROWS_TO_RETURN()) { # Get program ids associated with the specified year. $p4p_program_ids ||= GetMIPSProgramList($dbh, { YEARS => $years, FEDERALIDNUMBERS => $federal_id_numbers, }); $row_count = _GetMIPSPerformanceDataRowCount($dbh, { NPIS => $npis, FEDERALIDNUMBERS => $federal_id_numbers, P4PPROGRAMIDS => $p4p_program_ids, }); } @results = @{_AddReferenceMeasureNameForIA($dbh, {DATA=>\@results})} if grep { $_->{CATEGORY} eq 'IA' } @results; my $enabledversion = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_DASHBOARD_RELATIVE_PERFORMANCE', }) eq 'ON'; return { DATA => \@results, MIPSDASHBOARDRELATIVEPERFORMANCE => $enabledversion, HASMORERESULTS => $row_count > MAX_ROWS_TO_RETURN(), }; } ################################################################################ # _MIPSPerformanceDataSQL # # Description: # Version 2 of this function. Meant to improve performance by querying denormalized # table. # Generate SQL for MIPS measure performance data. # # Parameters: # A reference to a hash of parameters: # # Required: # P4PPROGRAMIDS - Listref, only retrieve data related to these programs. # # Optional: # ORDERBY - Listref, order the results by the following columns in the # specified order. # # FEDERALIDNUMBERS - Listref, only retrieve data related to these TINs. # # NPIS - Listref, only retrieve data for providers with these # NPIs. TINONLY is a special value, and will filter # down to TIN enrollments. # # Return Value: # A SQL::Select object representing the query. ################################################################################ sub _MIPSPerformanceDataSQL { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMIDS )], [qw( FEDERALIDNUMBERS NPIS ORDERBY P4PPROGRAMIDS )], ); my @orderby = @{ $args->{ORDERBY} || DEFAULT_ORDER_BY() }; my $federalidnumbers = $args->{FEDERALIDNUMBERS}; my $npis = $args->{NPIS}; my $p4pprogramids = $args->{P4PPROGRAMIDS}; my $relativeperformanceenabledversion = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_DASHBOARD_RELATIVE_PERFORMANCE', }) eq 'ON'; my @orderbysql; foreach my $orderby (@orderby) { my $sortkey = $orderby->{SortKey}; my $direction = $orderby->{Direction}; my @columns = @{ SORT_KEYS_TO_COLUMNS()->{$sortkey} || [$sortkey] }; if($relativeperformanceenabledversion) { push @orderbysql, (map { if ($_ ne 'SORTEDNUMERATOR' && $_ ne 'RELATIVEPERFORMANCE') { "performancedata.$_ $direction" } else { "$_ $direction" } } @columns); } else { push @orderbysql, (map { "performancedata.$_ $direction" } @columns); } } my $render_2021_dropdown = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_DASHBOARD_2021_DROPDOWN', }) eq 'ON'; my $sql = SQL::Select->new( )->Select( "performancedata.providerid", "performancedata.federalidnumber", "performancedata.denominator", "performancedata.numerator", "performancedata.excluded", "performancedata.performancerate", "performancedata.roundedperformancerate roundedpercentage", "performancedata.maxpoints", "performancedata.p4pprogramid", "performancedata.criteriacode", "performancedata.p4pmeasuresubscriptionid", "performancedata.category", "performancedata.npi", "performancedata.displayperformancerate displayperformancerate", "performancedata.isinversemeasure isinversemeasure", "case when performancedata.providerplclastname is not null and performancedata.providerplcfirstname is not null then performancedata.providerplclastname || ', ' || performancedata.providerplcfirstname else 'All' end providername", "CASE WHEN (performancedata.status = 'In Progress') THEN 'Not Satisfied' ELSE performancedata.status END status" )->From( "p4pmeasureperformance performancedata", )->Where( ["performancedata.p4pprogramid in (??)", $p4pprogramids], )->OrderBy(@orderbysql); if ( $federalidnumbers ) { $sql->Where( ["performancedata.federalidnumber in (??)", $federalidnumbers], ); } if($render_2021_dropdown) { $sql->Select( "CASE WHEN (performancedata.criteriacode like '%TASKCOMPLETION_SECURITYRISKRADIO%') THEN 'Security Risk Assessment' ELSE performancedata.measurename END AS measurename", ); $sql->Where( ["performancedata.criteriacode not like '%MEASURE_QUERYPDMP%'"], ); } else { $sql->Select( "CASE WHEN (performancedata.criteriacode = 'TASKCOMPLETION_SECURITYRISKRADIO') THEN 'Security Risk Assessment' ELSE performancedata.measurename END AS measurename", ); $sql->Where( ["performancedata.criteriacode != 'MEASURE_QUERYPDMP'"], ); } if ( $npis ) { my $npi_clause = ["performancedata.npi in (??)", $npis]; # TINONLY means we should looking for null NPIs, corresponding # to group enrollments. if ( List::AllUtils::any { $_ eq 'TINONLY' } @{ $npis || [] } ) { $npi_clause = SQL->Or( $npi_clause, "performancedata.npi is null", ); } $sql->Where( $npi_clause, ); } my $is_createencounter_toggle_enabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'QMII-7607-discount-createencounter', }) eq 'ON'; my $enrolledproviderssql = GetTINswithClinicalproviders("Athena::P4P::App::PerformanceData",{ CREATEENCOUNTERTOGGLE => $is_createencounter_toggle_enabled, }); $sql->Where(SQL->Or( SQL->And( ["performancedata.federalidnumber in (??)", $enrolledproviderssql], 'performancedata.providerid is null', ), "performancedata.providerid is not null", )); my $isrollouttoggleon = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'CORRECT_SUBMISSIONSCORE_ON_MIPSDB', }) eq 'ON'; if ($isrollouttoggleon) { $sql->Select( "CASE WHEN (exists (select 1 from p4psubmissiondata p4psubmissiondata, p4psubmission p4psubmission, p4psubmissiongroup p4psubmissiongroup where ( p4psubmissiondata.key = 'FINALCOMPOSITESCORE' and p4psubmissiondata.value = 0) AND ( p4psubmissiongroup.p4pprogramid = performancedata.p4pprogramid ) AND ( p4psubmissiongroup.federalidnumber = performancedata.federalidnumber ) AND ( p4psubmissiongroup.providerid = performancedata.providerid ) AND ( p4psubmissiongroup.id = p4psubmission.p4psubmissiongroupid ) AND ( p4psubmissiondata.p4psubmissionid = p4psubmission.id) )) THEN 0 ELSE performancedata.weightedmeasurepoints END measurepoints" ); } else { $sql->Select( "performancedata.weightedmeasurepoints measurepoints", ); } if ( $relativeperformanceenabledversion ) { $sql->Select( "case when p4pnetworkperformance.averagemeasureperformance is null and performancedata.p4pmeasuresubscriptionid is not null then -1 else p4pnetworkperformance.averagemeasureperformance end relativeperformance", "numerator as sortednumerator" ); $sql->From("p4pnetworkperformance p4pnetworkperformance"); $sql->Where( 'p4pnetworkperformance.p4pmeasuresubscriptionid(+) = performancedata.p4pmeasuresubscriptionid', ); } return $sql; } ########################################################################### # GetTINswithClinicalproviders # # Description: # Gets a SQL object that gets TINs with clinicals providers # a clinicals provider is defined as provider.createencounteratcheckinyn = Y, has a username, and NPI # # Parameters: # Required: # None. # Optional: # None. # # Return Value: # SQL::Select object ########################################################################### sub GetTINswithClinicalproviders { my ($class, $args) = @_; my $is_createencounter_toggle_enabled = $args->{CREATEENCOUNTERTOGGLE}; my $enrolledproviderssql = SQL::Select->new()->Distinct( )->Select( 'medicalgroup.federalidnumber providertin', )->From( 'provider', 'medicalgroup', 'providernumber', )->Joins( 'provider.medicalgroupid = medicalgroup.id (+)', 'providernumber.providerid = provider.id' )->Where( 'provider.username is not null', 'provider.deleted is null', 'providernumber.providernumbercategoryid = 454', 'providernumber.providernumber is not null', 'providernumber.deleted is null', 'medicalgroup.federalidnumber is not null', ); if(!$is_createencounter_toggle_enabled){ $enrolledproviderssql->Where( ['provider.createencounteroncheckinyn = ?', 'Y'], ); } return $enrolledproviderssql; } ################################################################################ # FetchMIPSMeasurePerformanceData # # Description: # Gets measure performance data for various MIPS programs for purposes of # denormalizing to the p4pmeasureperformance table. # # Required Parameters: # P4PPROGRAMID. Integer. Program to get measure performance data for. # # Optional Parameters: # PROVIDERID Integer. # FEDERALIDNUMBER String. # # Return value: # Arrayref of hashrefs containing P4PMEASUREPERFORMANCE columns as keys: # PROVIDERID # NPI # FEDERALIDNUMBER # DENOMINATOR # NUMERATOR # EXCLUDED # PERFORMANCERATE # ROUNDEDPERFORMANCERATE # WEIGHTEDMEASUREPOINTS # MAXPOINTS # P4PPROGRAMID # P4PMEASURESUBSCRIPTIONID # CRITERIACODE # MEASURENAME # CATEGORY # PROVIDERPLCFIRSTNAME # PROVIDERPLCLASTNAME ################################################################################ sub FetchMIPSMeasurePerformanceData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMID )], [qw( FEDERALIDNUMBER PROVIDERID ISMVP )], ); my $p4p_program_id = $args->{P4PPROGRAMID}; my $provider_id = $args->{PROVIDERID}; my $federal_id_number = $args->{FEDERALIDNUMBER}; my $is_mvp = $args->{ISMVP} || 0; my $npi_category_id = BusCall::Provider::GetNPIProviderNumberCategory(); my $context_id = Athena::Util::Database::SessionInfo($dbh)->{context}; my @npis; if ( $provider_id ) { my $npi_info = BusCall::Provider::GetProviderNumbers($dbh, { PROVIDERIDS => [ $provider_id ], CATEGORYIDS => [ $npi_category_id ], ALLDEPARTMENTS => 1, }); @npis = map { $_->{PROVIDERNUMBER} } @$npi_info; } my $start = time(); my @p4p_measure_performance_rows = _MIPSMeasurePerformanceSQL($dbh, { P4PPROGRAMID => $p4p_program_id, NPIS => @npis ? \@npis : undef, FEDERALIDNUMBERS => $federal_id_number ? [ $federal_id_number ] : undef, ISMVP => $is_mvp, })->TableHash($dbh); my $program_definitions_by_id = Athena::P4P::App::ProgramDefinition->new({ DBH => $dbh, })->GetProgramDefinition({ P4PPROGRAMIDS => [$p4p_program_id], }); my $timetofetchdata = time() - $start; Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( '{%.3f} FetchMIPSMeasurePerformanceData _MIPSMeasurePerformanceSQL ', $timetofetchdata, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4p_program_id, }, }); my $augmented_data = _AugmentWithMaxPoints({ PROGRAMDEFINITIONSBYID => $program_definitions_by_id, DATA => \@p4p_measure_performance_rows, }); my $fetch_criteria_info_enabled = GetMIPSACIToggleStatus($dbh, { P4PPROGRAMID => $p4p_program_id, ISMVP => $is_mvp, }); my $mipscategory = $is_mvp ? GetMVPCategory($dbh, $p4p_program_id) : GetMIPSCategory($dbh, $p4p_program_id); my $is_mips_ia = $mipscategory eq 'IA' ; if ($fetch_criteria_info_enabled) { $start = time(); $augmented_data = _AugmentWithCriteriaInfo($dbh, { P4PPROGRAMID => $p4p_program_id, DATA => $augmented_data, FEDERALIDNUMBERS => $federal_id_number, PROVIDERID => $provider_id, ISMVP => $is_mvp, }); $timetofetchdata = time() - $start; Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( '{%.3f} FetchMIPSMeasurePerformanceData _AugmentWithCriteriaInfo ', $timetofetchdata, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4p_program_id, }, }); } elsif($is_mips_ia){ $start = time(); $augmented_data = _GetMIPSIAMeasures($dbh, { P4PPROGRAMID => $p4p_program_id, FEDERALIDNUMBERS => $federal_id_number, PROVIDERID => $provider_id, ISMVP => $is_mvp, }); $timetofetchdata = time() - $start; Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( '{%.3f} FetchMIPSMeasurePerformanceData _GetMIPSIAMeasures ', $timetofetchdata, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4p_program_id, }, }); } return $augmented_data; } ################################################################################ # WriteMIPSMeasurePerformanceData # # Description: # Writes the measure performance data retrieved by FetchMIPSMeasurePerformanceData # to P4PMEASUREPERFORMANCE. Removes any rows from P4PMEASUREPERFORMANCE that # are no longer relevant (assumes that the Fetch function passes along ALL # currently-relevant entries in the table). # # Required Parameters: # DATA - Arrayref - List of hashrefs containing rows to insert into P4PMEASUREPERFORMANCE. # Should be return value of FetchMIPSMeasurePerformanceData. # # P4PPROGRAMID - Integer - Used to remove denormalized data for this program ID # that was not otherwise being updated per $args->{DATA}. # USERNAME - String - Username to make DB commits with. # # Optional Parameters: # PROVIDERID Integer. Only update this provider # FEDERALIDNUMBER String. Only update this TIN. Note: if FEDERALIDNUMBER # is provided but PROVIDERID is not, that is interpretted to indicate # that only the Group associated with that TIN should be updated. # # Return value: # None. ################################################################################ sub WriteMIPSMeasurePerformanceData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( DATA P4PPROGRAMID USERNAME )], [qw( PROVIDERID FEDERALIDNUMBER ISMVP )], ); my $data = $args->{DATA}; my $username = $args->{USERNAME}; my $federalidnumber = $args->{FEDERALIDNUMBER}; my $providerid = $args->{PROVIDERID}; my $p4pprogramid = $args->{P4PPROGRAMID}; my $is_mvp = $args->{ISMVP} || 0; my $is_mips_phr_enabled = GetMIPSACIToggleStatus($dbh, { P4PPROGRAMID => $p4pprogramid, ISMVP => $is_mvp, }); my $context_id = Athena::Util::Database::SessionInfo($dbh)->{context}; my $reporting_period = Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => $p4pprogramid, }); my $program_year = $reporting_period->{PROGRAMYEAR}; my $subgroupid; my $is_subgrp_toggleon = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'CLQI_5651_MVP_SUBGRP', }) eq 'ON'; my $is_subgroup; my $subgroup_map = BusCall::P4P::GetSubGroupMap($dbh); my $subgroup_programids = $subgroup_map->{SUBGROUPPROGRAMIDS}{$program_year}; if(Athena::Util::List::InList($p4pprogramid, @$subgroup_programids)){ $is_subgroup = 1; } if($is_subgrp_toggleon && $is_subgroup){ $subgroupid = Clinical::P4P::Utils::GetSubgroupId($dbh,{ P4PPROGRAMID => $p4pprogramid, }); } # Turn our DATA into a hash so that we can more easily search through it. my %hasheddata; foreach my $datum (@$data) { my $key = join(':', map { $datum->{$_} } @{_GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }) }); $hasheddata{$key} = 1; } # Retrieve all information currently in the denormalized table for this program ID my @selectcolumns = map { 'p4pmeasureperformance.' . $_ } @{_GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }) }; my $start = time(); my $existingdatasql = SQL::Select->new( )->Select( 'p4pmeasureperformance.id', @selectcolumns, )->From( 'p4pmeasureperformance', )->Where( ['p4pmeasureperformance.p4pprogramid = ?', $p4pprogramid], ); if ( $providerid ) { $existingdatasql->Where( ['p4pmeasureperformance.providerid = ?', $providerid], ); } if ( $federalidnumber ) { $existingdatasql->Where( ['p4pmeasureperformance.federalidnumber = ?', $federalidnumber], ); if ( ! $providerid ) { # If provider ID is not specified, but a TIN was, # we only want to look at data for the Group submission # associated with this TIN. $existingdatasql->Where( 'p4pmeasureperformance.providerid is null', ); } } my @existingdata = $existingdatasql->TableHash($dbh); my $timetogetexistingdata = time() - $start; Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( '{%.3f} Time taken to get existing data - complete - complete', $timetogetexistingdata, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, }, }); # For every existing row in the table, see if the passed-in DATA had an update # for this row, as identified by the same set of uniquely-identifying attributes. # If no match is found, this represents a state such as a provider no longer being # enrolled in a measure, so mark this row for deletion. my @rowstodelete; foreach my $existingdatum (@existingdata) { my $key = join(':', map { $existingdatum->{$_} } @{_GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }) }); if (!$hasheddata{$key}) { push @rowstodelete, $existingdatum; } } my $is_optimize_write_measureperf_enabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'QPSU-4241-SUBMISSION-CHECK-DB-62-FIX', }) eq 'ON'; if(!$is_optimize_write_measureperf_enabled) { if (@$data) { _MergeNewWithExistingData($dbh, { DATA => $data, ISMIPSPHRENABLED => $is_mips_phr_enabled, P4PPROGRAMID => $p4pprogramid, USERNAME => $username, SUBGROUPID => $subgroupid, }); } } else { # We are going to do a Merge in case of group programs because providerid which is a part of the primary key will be null in this case and hence update will not work on this case # in case of individuals we will do an Update and Add by determining which operation needs to be done my $entitytype = $is_mvp ? Athena::P4P::App::PerformanceData::GetMVPEntityType($dbh, $p4pprogramid) : Athena::P4P::App::PerformanceData::GetMIPSEntityType($dbh, $p4pprogramid); # Merge the adds & updates, if there is any data to merge if (@$data && $entitytype eq 'GROUP') { _MergeNewWithExistingData($dbh, { DATA => $data, ISMIPSPHRENABLED => $is_mips_phr_enabled, P4PPROGRAMID => $p4pprogramid, USERNAME => $username, SUBGROUPID => $subgroupid, }); } else { # Update and add my %existinghasheddata; my @rowstoupdate; my @rowstoadd; foreach my $existingdatum (@existingdata) { my $key = join(':', map { $existingdatum->{$_} } @{_GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }) }); $existinghasheddata{$key} = 1; } foreach my $datum (@$data) { my $key = join(':', map { $datum->{$_} } @{_GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }) }); if($subgroupid ne undef){ $datum->{SUBGROUPID} = $subgroupid; } if(!$existinghasheddata{$key}) { push @rowstoadd, $datum; } else { push @rowstoupdate,$datum; } } my $rowsaffectedthroughupdate = 0; my $rowsaffectedthroughadd = 0; if (@rowstoupdate) { while(@rowstoupdate) { my @newdata = splice (@rowstoupdate, 0, 500); my $begin = time(); $rowsaffectedthroughupdate += Athena::Util::SQL::ProcessTable($dbh, { OPERATION => 'Update', COLUMNNAMES => P4PMEASUREPERFORMANCE_COLUMNS(), PRIMARYKEY => _GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }), TABLENAME => 'P4PMEASUREPERFORMANCE', TABLEROWS => \@newdata, USERNAME => $username, }); my $timetakenforupdate = time() - $begin; my $count = scalar(@newdata); Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( '{%.3f} Time taken for update - complete - complete', $timetakenforupdate, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( 'No of records for update::'.$count, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); } } Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( 'Total records for update::'.$rowsaffectedthroughupdate, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); # Add necessary rows if (@rowstoadd) { while(@rowstoadd) { my @newdata = splice (@rowstoadd, 0, 500); my $begin = time(); $rowsaffectedthroughadd += Athena::Util::SQL::ProcessTable($dbh, { OPERATION => 'Add', PRIMARYKEY => _GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }), COLUMNNAMES => P4PMEASUREPERFORMANCE_COLUMNS(), TABLENAME => 'P4PMEASUREPERFORMANCE', TABLEROWS => \@newdata, USERNAME => $username, }); my $timetakenforupdate = time() - $begin; my $count = scalar(@newdata); Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( '{%.3f} Time taken for add - complete - complete', $timetakenforupdate, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( 'No of records for add::'.$count, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); } } Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( 'Total records for add::'.$rowsaffectedthroughadd, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); } } # Delete unnecessary rows Athena::Util::SQL::ProcessTable($dbh, { OPERATION => 'Delete', TABLENAME => 'P4PMEASUREPERFORMANCE', TABLEROWS => \@rowstodelete, USERNAME => $username, }); return; } ################################################################################ # _MIPSMeasurePerformanceSQL # # Description: # Creates an SQL object with measure performance data, starting from the # MeasureDataSQL to pull submission table data for certain keys and adding in # some other useful fields. # # Required Parameters: # # P4PPROGRAMID Integer. Program to get measure performance data for. # # Optional Parameters: # # NPIS Arrayref. NPIs for which to retreive data # FEDERALIDNUMBERS Arrayref. TINs for which to retreive data # # Return value: # An SQL::Select object. ################################################################################ sub _MIPSMeasurePerformanceSQL { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMID )], [qw( NPIS FEDERALIDNUMBERS ISMVP )], ); my $p4pprogramid = $args->{P4PPROGRAMID}; my $npis = $args->{NPIS}; my $federalidnumbers = $args->{FEDERALIDNUMBERS}; my $is_mvp = $args->{ISMVP} || 0; my $npicategoryid = BusCall::Provider::GetNPIProviderNumberCategory(); my @keys; my $fetch_mips_aci_phr_enabled = GetMIPSACIToggleStatus($dbh, { P4PPROGRAMID => $p4pprogramid, ISMVP => $is_mvp, }); my $mipscategory = $is_mvp ? GetMVPCategory($dbh, $p4pprogramid) : GetMIPSCategory($dbh, $p4pprogramid); my $is_mips_ia = $mipscategory eq 'IA'; if ($fetch_mips_aci_phr_enabled || $is_mips_ia ) { @keys = qw( DENOMINATOR EXCLUDED NUMERATOR PERFORMANCERATE ROUNDEDPERFORMANCERATE WEIGHTEDMEASUREPOINTS FINALMEASUREPOINTS DISPLAYPERFORMANCERATE ISINVERSEMEASURE STATUS MAXPOINTS ); } else { @keys = qw( DENOMINATOR EXCLUDED NUMERATOR PERFORMANCERATE ROUNDEDPERFORMANCERATE WEIGHTEDMEASUREPOINTS FINALMEASUREPOINTS DISPLAYPERFORMANCERATE ISINVERSEMEASURE MAXPOINTS ); } my $sql = Clinical::P4P::Persistence::MultiProviderSubmission->GetMeasureDataSQL({ INCLUDEFEDERALIDNUMBERS => 1, INCLUDEPROGRAMIDS => 1, INCLUDEPROVIDERIDS => 1, KEYS => \@keys, P4PPROGRAMID => $p4pprogramid, }); my $category = $is_mvp ? GetMVPCategory($dbh, $p4pprogramid) : GetMIPSCategory($dbh, $p4pprogramid); my $get_measure_data_index = "index(pd P4PDENORMENROLL_PRGPRFESUBME)"; if (defined Athena::Conf::AthenaNet::InternalServices('qupro')->{get_measure_data_index_outer}) { $get_measure_data_index = Athena::Conf::AthenaNet::InternalServices('qupro')->{get_measure_data_index_outer}; } my $performancedatasql = SQL::Select->new( )->Distinct( )->Select( "performancedata.providerid", "providernumber.providernumber npi", "performancedata.federalidnumber", "performancedata.denominator", "performancedata.numerator", "performancedata.excluded", "performancedata.performancerate", "performancedata.roundedperformancerate", "performancedata.p4pprogramid", "performancedata.p4pmeasuresubscriptionid", "performancedata.criteriacode", "performancedata.displayperformancerate", "performancedata.isinversemeasure", "p4pmeasure.id p4pmeasureid", "p4pmeasure.shortname measurename", ["? category", $category], "provider.plcfirstname providerplcfirstname", "provider.plclastname providerplclastname", )->From( ["(??) performancedata", $sql], "p4pmeasure", "p4pdenormalizedenrollment pd", "provider", "providernumber", )->Joins( "performancedata.p4pmeasureid = p4pmeasure.id", "performancedata.p4pprogramid = pd.p4pprogramid", "performancedata.p4pmeasuresubscriptionid = pd.p4pmeasuresubscriptionid", "performancedata.providerid = provider.id (+)", "provider.id = providernumber.providerid (+)", )->Where( ["performancedata.p4pprogramid = ?", $p4pprogramid], SQL->Or( "performancedata.providerid is null", "performancedata.providerid = pd.providerid", ), SQL->Or( "pd.federalidnumber is null", "performancedata.federalidnumber = pd.federalidnumber", ), "nvl(pd.hiddenyn, 'N') = 'N'", "providernumber.deleted (+) is null", "pd.deleted is null", "pd.guidelines = '0'", ["providernumber.providernumbercategoryid (+) = ?", $npicategoryid], ); if($get_measure_data_index ne 'OFF') { $performancedatasql->Hints( $get_measure_data_index, ); } if ($fetch_mips_aci_phr_enabled || $is_mips_ia) { $performancedatasql->Select( "performancedata.status", ); } $performancedatasql->Select( # Prefer final scores over preview scores. Athena::Util::Text::OneLine(qq{ nvl( performancedata.finalmeasurepoints, performancedata.weightedmeasurepoints ) weightedmeasurepoints }), ); if ( $npis ) { $performancedatasql->Where( ["providernumber.providernumber in (??)", $npis], ); } if ( $federalidnumbers ) { $performancedatasql->Where( ["performancedata.federalidnumber in (??)", $federalidnumbers], ); if ( ! $npis ) { # If NPIs are not specified but TINs are, then assume we're # only looking for Group performance data. $performancedatasql->Where( "performancedata.providerid is null", ); } } my $enabledversion = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_DISPLAY_MAX_SCORES_FROM_CMS', }) eq 'ON'; if ( $enabledversion ) { $performancedatasql->Select( "performancedata.maxpoints", ); } return $performancedatasql; } sub _AugmentWithMaxPoints { my ($args) = @_; my $program_definitions_by_id = $args->{PROGRAMDEFINITIONSBYID}; my $data = $args->{DATA}; foreach my $row (@$data) { my $program_id = $row->{P4PPROGRAMID}; my $measure_subscription_id = $row->{P4PMEASURESUBSCRIPTIONID}; my $criteria_code = $row->{CRITERIACODE}; my $program_definition = $program_definitions_by_id->{$program_id}; my $measure = $program_definition->MeasureByP4PMeasureSubscriptionID($measure_subscription_id); if ($measure) { my $criterion = List::Util::first { $_->CriteriaCode() eq $criteria_code } @{ $measure->Criteria() }; if ($criterion) { my $points_multiplier = $criterion->PointsMultiplier(); $row->{MAXPOINTS} = $row->{MAXPOINTS} ? $row->{MAXPOINTS} : DEFAULT_MAX_POINTS() * $points_multiplier; } } } return $data; } ########################################################################################## # _AugmentWithCriteriaInfo # # Description: # Add PHR related submission data to be stored in p4pmeasureperformance # # Parameters: # Required: # None # Optional: # NPIS ListRef[String] NPI numbers to filter to. TINONLY is a special # value, and will filter down to TIN enrollments. # P4PPROGRAMID ListRef[Integer] # FEDERALIDNUMBERS ListRef[String] # PROVIDERID ListRef[Integer] # DATA ListRef[String] # # Return Value: # ListRef[String] ########################################################################################## sub _AugmentWithCriteriaInfo { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( )], [qw( NPIS FEDERALIDNUMBERS PROVIDERID P4PPROGRAMID DATA ISMVP )], ); my $p4pprogramid = $args->{P4PPROGRAMID}; my $data = $args->{DATA}; my $provider_id = $args->{PROVIDERID}; my $federal_id_number = $args->{FEDERALIDNUMBERS}; my $is_mvp = $args->{ISMVP} || 0; my $reporting_period = Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => $p4pprogramid, }); my $program_year = $reporting_period->{PROGRAMYEAR}; my @keys = qw( DENOMINATOR EXCLUDED NUMERATOR PERFORMANCERATE ROUNDEDPERFORMANCERATE FINALMEASUREPOINTS WEIGHTEDMEASUREPOINTS DISPLAYPERFORMANCERATE ISINVERSEMEASURE STATUS MAXPOINTS ); my $mipscategory = $is_mvp ? GetMVPCategory($dbh, $p4pprogramid) : GetMIPSCategory($dbh, $p4pprogramid); my $entitytype = $is_mvp ? GetMVPEntityType($dbh, $p4pprogramid) : GetMIPSEntityType($dbh, $p4pprogramid); my $get_measure_data_index = "index(pde P4PDENORMENROLL_PRGPRFESUBME)"; if (defined Athena::Conf::AthenaNet::InternalServices('qupro')->{get_measure_data_index_ctr_info}) { $get_measure_data_index = Athena::Conf::AthenaNet::InternalServices('qupro')->{get_measure_data_index_ctr_info}; } my $sql = Clinical::P4P::Persistence::MultiProviderSubmission->GetMeasureDataSQL({ INCLUDEFEDERALIDNUMBERS => 1, INCLUDEPROGRAMIDS => 1, INCLUDEPROVIDERIDS => 1, INCLUDEPHRDATA => 1, P4PPROGRAMID => $p4pprogramid, PROGRAMCATEGORY => $mipscategory, KEYS => \@keys, ENTITYTYPE => $entitytype, EXCLUDEENROLLMENTCHECK => 1, }); if ( $provider_id ) { $sql->Where( ["p4psubmissiongroup.providerid in (??)", $provider_id ], ); } elsif ( $federal_id_number ) { $sql->Where( ["p4psubmissiongroup.federalidnumber in (??)", $federal_id_number ], ); } my @allmeasuredata; if($program_year > 2022){ # 1.All taskcompletion measures now have a dummy p4pmeasure id and they are also enrolled , hence we can now filter by enrollment # 2. Some measures like PDMP , MDD have one measure and one taskcompletion measure in such cases enrolment in on the measure and to determine enrolment # we are using the ARGUMENTS.P4PMEASUREID in p4ppdcriteriadata my $performancedatasql = SQL::Select->new( )->Distinct( )->Select( "performancedata.*", )->From( ["(??) performancedata", $sql], "p4pdenormalizedenrollment pde", "p4ppdcriteria", "p4ppdcriteriadata", )->Where( ["pde.p4pprogramid in (??)", $p4pprogramid], "performancedata.p4pprogramid = pde.p4pprogramid", SQL->Or( "performancedata.providerid is null", "performancedata.providerid = pde.providerid", ), SQL->Or( "pde.federalidnumber is null", "performancedata.federalidnumber = pde.federalidnumber", ), "p4ppdcriteria.id = p4ppdcriteriadata.p4ppdcriteriaid", "performancedata.p4ppdcriteriaid = p4ppdcriteria.id", SQL->Or( "performancedata.p4pmeasuresubscriptionid = pde.p4pmeasuresubscriptionid", "performancedata.p4pmeasuresubscriptionid is null AND p4ppdcriteriadata.key = 'ARGUMENTS.P4PMEASUREID' AND pde.p4pmeasureid = p4ppdcriteriadata.value ", ), "nvl(pde.hiddenyn, 'N') = 'N'", "pde.deleted is null", "pde.guidelines = '0'", ); if($get_measure_data_index ne 'OFF') { $performancedatasql->Hints( $get_measure_data_index, ); } @allmeasuredata = $performancedatasql->TableHash($dbh); } else{ @allmeasuredata = $sql->TableHash($dbh); } @allmeasuredata = grep { (!($_->{PROGRAMKEY} =~ /EXCLUSION/ || $_->{PROGRAMKEY} =~ /PRE/ || $_->{PROGRAMKEY} =~ /PROD/) && ( $_->{STATUS} eq 'Satisfied' || $_->{STATUS} eq 'In Progress' || $_->{STATUS} eq 'Excluded')) } @allmeasuredata; # In case of PI we have _MIPSMeasurePerformanceSQL which fetches all the enrolled measures, because now all PI taskcompletion measures have a dummy measure id # we already have these taskcompletion information.Only in case of PDMP we need to do this check @$data = grep {!( $_->{P4PMEASURESUBSCRIPTIONID} ne undef && $_->{CRITERIACODE} =~ /^TASKCOMPLETION/ ) } @$data; my $phrdata = _GetCombinedPhrMeasures($dbh, { MEASUREDATA => \@allmeasuredata, P4PPROGRAMID => $p4pprogramid, ENTITYTYPE => $entitytype, }); push @$phrdata, @$data; foreach my $datahash(@$phrdata){ if($datahash->{FINALMEASUREPOINTS} && $datahash->{FINALMEASUREPOINTS} ne undef){ $datahash->{WEIGHTEDMEASUREPOINTS} = $datahash->{FINALMEASUREPOINTS}; } } return $phrdata; } ########################################################################################## # _GetCombinedPhrMeasures # # Description: # Checks the max satisfying count for PHR measures # and # # Parameters: # Required: # MEASUREDATA : the list of PHR measures # P4PPROGRAMID : MIPS PI Program id # ENTITYTYPE : GROUP / INDIVIDUAL # Optional: # None # # Return Value: # Combined Measures details with status ########################################################################################## sub _GetCombinedPhrMeasures { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( MEASUREDATA P4PPROGRAMID ENTITYTYPE )], [qw( )], ); my $measure_data = $args->{MEASUREDATA}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $entity_type = $args->{ENTITYTYPE}; my $program_definition = BusCall::P4P::GetProgramDefinition($dbh, { P4PPROGRAMID => $p4p_program_id, }); my @measure_results; my %measure_data_by_providerid_or_tin; if( $entity_type eq 'INDIVIDUAL'){ %measure_data_by_providerid_or_tin = Athena::Util::Hash::HashGroupBy(['PROVIDERID'], @$measure_data); } else{ %measure_data_by_providerid_or_tin = Athena::Util::Hash::HashGroupBy(['FEDERALIDNUMBER'], @$measure_data); } foreach my $providerid_or_tin (sort keys %measure_data_by_providerid_or_tin) { my $measuredata = $measure_data_by_providerid_or_tin{$providerid_or_tin}; my %measure_data_by_program_key = Athena::Util::Hash::HashGroupBy(['PROGRAMKEY'], @$measuredata); foreach my $program_key (sort keys %measure_data_by_program_key) { my $pd_measure = $program_definition->MeasureByProgramKeys()->{$program_key}; my $measure_result = Athena::P4P::App::PerformanceData::GetMIPSMeasureSubmissionData({ MEASUREDATA => $measure_data_by_program_key{$program_key}, PDMEASURE => $pd_measure, }); my $eachmeasuredata = $measure_data_by_program_key{$program_key}[0]; if ( $measure_result->{ISCOMBINEDPERFORMANCEMEASURE} && $measure_result->{MAXIMUMSATISFYINGREGISTRIES} ) { my $satisfying_count = 0; foreach my $sub_measure ( @{ $measure_result->{VALUES} || [] } ) { if ( $sub_measure->{BOOLEANSATISFIED} ) { $satisfying_count++; } } if ( $satisfying_count >= $measure_result->{MAXIMUMSATISFYINGREGISTRIES} ) { $eachmeasuredata->{STATUS} = 'Satisfied'; } else{ $eachmeasuredata->{STATUS} = 'In Progress'; } } $eachmeasuredata->{PROGRAMKEY} = $program_key; if( $entity_type eq 'INDIVIDUAL'){ $eachmeasuredata->{PROVIDERID} = $providerid_or_tin; } else{ $eachmeasuredata->{FEDERALIDNUMBER} = $providerid_or_tin; } push @measure_results, $eachmeasuredata; } } return \@measure_results; } ########################################################################################## # _GetMIPSPerformanceDataRowCount # # Description: # Gets a count of the number of measure performance rows for a specified set of filters. # # Helper function for GetMIPSPerformanceDataWithRowCount. # # Parameters: # Required: # None # Optional: # NPIS ListRef[String] NPI numbers to filter to. TINONLY is a special # value, and will filter down to TIN enrollments. # P4PPROGRAMIDS ListRef[Integer] # FEDERALIDNUMBERS ListRef[String] # # Return Value: # Integer - row count. ########################################################################################## sub _GetMIPSPerformanceDataRowCount { my ($dbh, $args) = @_; my $npis = $args->{NPIS}; my $sql = SQL->Select( 'count(p4pmeasureperformance.id)', )->From( 'p4pmeasureperformance', ); if ($args->{P4PPROGRAMIDS}) { $sql->Where(['p4pmeasureperformance.p4pprogramid in (??)', $args->{P4PPROGRAMIDS}]); } if ( $npis ) { my $npi_clause = ["p4pmeasureperformance.npi in (??)", $npis]; # TINONLY means we should looking for null NPIs, corresponding # to group enrollments. if ( List::AllUtils::any { $_ eq 'TINONLY' } @{ $npis || [] } ) { $npi_clause = SQL->Or( $npi_clause, "p4pmeasureperformance.npi is null", ); } $sql->Where( $npi_clause, ); } if ($args->{FEDERALIDNUMBERS}) { $sql->Where(['p4pmeasureperformance.federalidnumber in (??)', $args->{FEDERALIDNUMBERS}]); } my ($count) = $sql->ColumnValues($dbh); return $count; } ################################################################################ # GetMIPSSubmissionData # # Description: # Gets measure performance data for various MIPS programs along with metadata # necessary to include when submitting these measures to CMS. # # Parameters: # Required: # FEDERALIDNUMBER Scalar TIN for which to retreive data # Optional: # PROVIDERID Scalar ID of the provider for whom to retreive data # MIPSCOMPONENTS Arrayref Only retrieve data related to these MIPS components. # Must valid components (categories) for that program year. # Defaults to all valid components. # YEAR Integer The program year that applies. Defaults to 2017. # ENTITYTYPE String INDIVIDUAL or GROUP. Defaults to INDIVIDUAL. # ROLLING90DAYREQUEST Boolean. # # Returns: # Hashref with the following structure # [mips component name] => [ # arrayref of measure results # ] # # See GetMIPSComponentSubmissionData for the structure of the arrayref of # measure results ################################################################################ sub GetMIPSSubmissionData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( FEDERALIDNUMBER )], [qw( MIPSCOMPONENTS PROVIDERID YEAR ENTITYTYPE ROLLING90DAYREQUEST WINDOWSTART P4PPROGRAMID ISMVP )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $year = $args->{YEAR} // 2017; _AssertValidYear($year); my $is_mvp = $args->{ISMVP} || 0; my $mips_components = $args->{MIPSCOMPONENTS} || _SupportedCategoriesForYear({ YEAR => $year, ISMVP => $is_mvp, }); my $provider_id = $args->{PROVIDERID}; my $entity_type = $args->{ENTITYTYPE}; my $rolling90dayrequest = $args->{ROLLING90DAYREQUEST}; my $windowstart = $args->{WINDOWSTART}; my $programid = $args->{P4PPROGRAMID}; if ( ! defined $entity_type ) { # Default entity_type to INDIVIDUAL if a provider is specified, # otherwise assume we're dealing with a group. if ( $provider_id ) { $entity_type = 'INDIVIDUAL'; } else { $entity_type = 'GROUP'; } } _AssertValidEntityType($entity_type); my %result_structure; my $component_data; my $istoggleon = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => "CLQI_2022_MIPS_ROLLING_90_DAYS", }) eq "ON"; foreach my $mips_component (@$mips_components) { my $p4p_program_id = GetMIPSProgram($dbh, { CATEGORY => $mips_component, YEAR => $year, ENTITYTYPE => $entity_type, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, ISMVP => $is_mvp, }); $p4p_program_id = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $p4p_program_id, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, ENTITYTYPE => $entity_type, MIPSCOMPONENT => $mips_component, YEAR => $year, ISMVP => $is_mvp, }); if ($rolling90dayrequest && $istoggleon){ $component_data = Athena::P4P::App::PerformanceData::GetMIPSComponentSubmissionData($dbh, { FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $programid, PROVIDERID => $provider_id, ROLLING90DAYREQUEST => $rolling90dayrequest, WINDOWSTART => $windowstart, }); } else { $component_data = Athena::P4P::App::PerformanceData::GetMIPSComponentSubmissionData($dbh, { FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, ROLLING90DAYREQUEST => $rolling90dayrequest, WINDOWSTART => $windowstart, }); } $result_structure{$mips_component} = $component_data; } return \%result_structure; } ################################################################################ # GetMIPSComponentSubmissionData # # Description: # Gets measure performance data for a single component of MIPS along with metadata # necessary to include when submitting these measures to CMS. # # Parameters: # Required: # FEDERALIDNUMBER Scalar TIN for which to retreive data # P4PPROGRAMID Scalar The p4pprogramid for the MIPS component # for which to retreive data. # Optional: # PROVIDERID Scalar Provider for whom to retreive data # # Returns: # Arrayref of results for each measure in which the TIN or provider is enrolled. # Each element of the array is a hashref as returned by GetMIPSMeasureSubmissionData. ################################################################################ sub GetMIPSComponentSubmissionData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( FEDERALIDNUMBER P4PPROGRAMID )], [qw( PROVIDERID ROLLING90DAYREQUEST WINDOWSTART )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $rolling90dayrequest = $args->{ROLLING90DAYREQUEST}; my $windowstart = $args->{WINDOWSTART}; my $program_definition = BusCall::P4P::GetProgramDefinition($dbh, { P4PPROGRAMID => $p4p_program_id, }); my $measure_data; my $istoggleon = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => "CLQI_2022_MIPS_ROLLING_90_DAYS", }) eq "ON"; my %measure_data_by_program_key; if ( $rolling90dayrequest && $istoggleon) { $measure_data = Athena::P4P::App::Rolling90Days::GetMeasureDataForRolling90Days($dbh,{ P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, WINDOWSTART => $windowstart, }); %measure_data_by_program_key = Athena::Util::Hash::HashGroupBy(['KEY'], @$measure_data); } else { $measure_data = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, })->GetMeasureData({ KEYS => [qw( DENOMEX DENOMINATOR ENROLLED EXCLUDED IPP NUMERATOR STATUS )], }) || []; # We only want to submit measures for which the provider is actually enrolled my @enrolled_measures = grep { $_->{ENROLLED} } @$measure_data; %measure_data_by_program_key = Athena::Util::Hash::HashGroupBy(['PROGRAMKEY'], @enrolled_measures); } my @measure_results; foreach my $program_key (sort keys %measure_data_by_program_key) { my $pd_measure = $program_definition->MeasureByProgramKeys()->{$program_key}; # If we have data for the provider that is not recognized as a measure in the program # definition, we'll just skip that element rather than ISE. next unless $pd_measure; my $measure_result = Athena::P4P::App::PerformanceData::GetMIPSMeasureSubmissionData({ MEASUREDATA => $measure_data_by_program_key{$program_key}, PDMEASURE => $pd_measure, }); push @measure_results, $measure_result; } return \@measure_results; } ################################################################################ # GetMIPSMeasureSubmissionData # # Description: # Gets measure performance data for a single program definition measure along with # metadata necessary for submitting the measure to CMS. # # Parameters: # Required: # MEASUREDATA Hashref mapping PROGRAMKEY to an array of results for the # measure's criteria # PDMEASURE Athena::P4P::Entity::ProgramDefinition::Measure # Optional: # None. # # Returns: # Hashref with the following keys: # HASMULTIPLEPERFORMANCERATES: Boolean indicating whether the measure is a # multiPerformanceRate measure # ISBOOLEANMEASURE: Boolean indicating a boolean metric type. # ISCOMBINEDPERFORMANCEMEASURE:Boolean indicating the measure criteria's performance # should be combined for submission. # QPPMEASUREID: The CMS QPP identifier for the measure # PROGRAMKEY: The athena-defined program key for the program # definition measure # SUBMISSIONMETHOD: The submission method of the measure; will generally # only be populated for Quality component measures # VALUES: An arrayref of the form returned by GetMIPSCriteriaSubmissionData ################################################################################ sub GetMIPSMeasureSubmissionData { my ($args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( MEASUREDATA PDMEASURE )], [qw( )], ); my @measure_data = @{ $args->{MEASUREDATA} || [] }; my $pd_measure = $args->{PDMEASURE}; my $performance_values = Athena::P4P::App::PerformanceData::GetMIPSCriteriaSubmissionData({ CRITERIADATA => \@measure_data, PDCRITERIA => $pd_measure->Criteria(), ISBOOLEANMEASURE => $pd_measure->IsBooleanMeasure(), }); return { HASMULTIPLEPERFORMANCERATES => $pd_measure->HasMultiplePerformanceRates(), ISBOOLEANMEASURE => $pd_measure->IsBooleanMeasure(), ISCOMBINEDPERFORMANCEMEASURE => $pd_measure->IsCombinedPerformanceMeasure(), QPPMEASUREID => $pd_measure->QPPMeasureID, PROGRAMKEY => $pd_measure->ProgramKey(), SUBMISSIONMETHOD => $pd_measure->SubmissionMethod(), MAXIMUMSATISFYINGREGISTRIES => $pd_measure->MaximumSatisfyingRegistries(), REQUIREDSUBMEASURES => $pd_measure->RequiredSubMeasures(), VALUES => $performance_values, }; } ################################################################################ # GetMIPSCriteriaSubmissionData # # Description: # Gets performance data for all multiple criteria of a single program definition # measure, along with metadata necessary for submitting the data to CMS. # # Parameters: # Required: # CRITERIADATA Arrayref of hashrefs, each representing a provider's performance # rate in a particular criterion # PDCRITERION Arrayref[Athena::P4P::Entity::ProgramDefinition::Criteria] # # Optional: # ISBOOLEANMEASURE Boolean, indicates that this PD should return a boolean # value as opposed to numeric NUMERATOR, DENOMINATOR, etc. # # Returns: # Arrayref, where each element is a hashref in the format returned by # GetMIPSCriterionSubmissionData. ################################################################################ sub GetMIPSCriteriaSubmissionData { my ($args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( CRITERIADATA PDCRITERIA )], [qw( ISBOOLEANMEASURE )], ); my @criteria_data = @{ $args->{CRITERIADATA} }; my @pd_criteria = @{ $args->{PDCRITERIA} }; my %criteria_by_code = map { $_->CriteriaCode() => $_ } @pd_criteria; my @measure_values; foreach my $criterion_data (@criteria_data) { my $pd_criterion = $criteria_by_code{$criterion_data->{CRITERIACODE}}; # If we have data for the provider that is not recognized as a criterion in the program # definition, we'll just skip that element rather than ISE. next unless ( $pd_criterion # An indication that this is not a particular stratification layer && $pd_criterion->IsTotalPerformanceRate() ); push @measure_values, Athena::P4P::App::PerformanceData::GetMIPSCriterionSubmissionData({ CRITERIONDATA => $criterion_data, PDCRITERION => $pd_criterion, ISBOOLEANMEASURE => $args->{ISBOOLEANMEASURE}, }); } return \@measure_values; } ################################################################################ # GetMIPSCriterionSubmissionData # # Description: # Gets measure performance data for a single program definition criterion along with # metadata necessary for submitting the measure to CMS. # # Parameters: # Required: # CRITERIONDATA Hashref of a provider's performance rate in a particular criterion # PDCRITERION Athena::P4P::Entity::ProgramDefinition::Criteria # # Optional: # ISBOOLEANMEASURE Boolean, indicates that this PD should return a boolean # value as opposed to numeric NUMERATOR, DENOMINATOR, etc. # # Returns: # Hashref with the following keys: # QPPSTRATUMNAME: The CMS QPP identifier for the stratum (will be # null for measures that have only a single performance rate) # CRITERIACODE: The athena-defined critera code for the program definition # measure criterion # # The following keys report any populations computed by the submission check: # NUMERATOR # DENOMINATOR # EXCLUDED # DENOMEX # IPP ################################################################################ sub GetMIPSCriterionSubmissionData { my ($args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( CRITERIONDATA PDCRITERION )], [qw( ISBOOLEANMEASURE )], ); my $criterion_data = $args->{CRITERIONDATA}; my $pd_criterion = $args->{PDCRITERION}; my $is_boolean_measure = $args->{ISBOOLEANMEASURE}; # Metadata about the particular criterion result my %criterion_result = ( CRITERIACODE => $criterion_data->{CRITERIACODE}, QPPSTRATUMNAME => $pd_criterion->QPPStratumName() || '', ); if ($is_boolean_measure) { # Boolean Measures have no numeric fields, just true or false. $criterion_result{BOOLEANSATISFIED} = $criterion_data->{STATUS} eq 'Satisfied' ? 1 : ''; } else { # Non-boolean measures need to send different buckets of numeric data. my @numeric_fields = qw( NUMERATOR DENOMINATOR EXCLUDED DENOMEX IPP ); # Include each relevant performance rate, assigning 0 if there is no data foreach my $field (@numeric_fields) { $criterion_result{$field} = $criterion_data->{$field} || 0; } } return \%criterion_result; } ################################################################################ # GetMIPSSubmissionReadiness # # Description: # Given a provider and/or TIN, this function figures out for each MIPS component # and -- where applicable -- submission method, whether or not that component # and submission method are ready to be submitted officially for scoring. This # takes into account: # 1) Whether the enrolled entity has been blacklisted # 2) Whether the enrolled entity's data has been snapshotted and attested (either # by the provider or by Athena) # 3) Whether the component & submission method have met minimum reporting # requirements # 4) Whether the component & submission method have not yet been submitted already # # Parameters: # Required: # YEAR Scalar Year to check the submission readiness for. # At least one of: # FEDERALIDNUMBER Scalar TIN for which to retreive data # PROVIDERID Scalar ID of the provider for whom to retreive data # # Optional: # MIPSCOMPONENTS Arrayref Only retrieve data related to these MIPS components. # Must be a valid component (category) for that program year. # Defaults to all valid components. # # Returns: # Hashref, where the keys are uppercased concatenations of the MIPS component name # and -- where applicable -- an underscore and the submission method (e.g., QUALITY_EHR). # The values are Boolean, representing whether or not that component # (and submission method) are ready for submission. # ################################################################################ sub GetMIPSSubmissionReadiness { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( YEAR ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }], [qw( MIPSCOMPONENTS ISMVP )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $year = $args->{YEAR}; my $mips_components = $args->{MIPSCOMPONENTS} || _SupportedCategoriesForYear({ YEAR => $year, }); my $provider_id = $args->{PROVIDERID}; my $eligibilitycheckcalled = 0; my $eligibile_to_submit = 1; my $mipseligibilitystatus; my $iseligibleforsubmission = 1; my $composite_score_threshold = 81; my $return_status_quality; if (defined Athena::Conf::AthenaNet::InternalServices('qupro')->{composite_score_threshold}){ $composite_score_threshold = Athena::Conf::AthenaNet::InternalServices('qupro')->{composite_score_threshold}; } my %return_structure; my $submission_blocking_reason = ''; foreach my $mips_component (@$mips_components) { next if ! $mips_component; $eligibile_to_submit = 1; my $p4p_program_id = Athena::P4P::App::PerformanceData::GetMIPSProgram($dbh, { CATEGORY => $mips_component, YEAR => $year, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, ISMVP => $args->{ISMVP}, }); $p4p_program_id = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $p4p_program_id, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => $mips_component, YEAR => $year, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', ISMVP => $args->{ISMVP}, }); Athena::Util::Assert::Assert($p4p_program_id, "$mips_component is not a valid MIPS component"); # Get all expected keys for this MIPS component my @satisfied_component_keys = @{ Athena::P4P::App::PerformanceData::GetSatisfiedComponentKeys({ MIPSCOMPONENT => $mips_component }) }; # Next, get the attestation status required for submitting this component my $submission_module = Clinical::P4P::Utils::GetProgramModule($dbh, { P4PPROGRAMID => $p4p_program_id, TYPE => 'Submission', }); my $component_key_attestation_status_required = $submission_module->GetAttestationStatusClassForSubmission($dbh, { COMPONENTKEYS => \@satisfied_component_keys, }); my $program_submission = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); if ( $program_submission->NoDenormalizedData() ) { # If the submission object does not exist in the database, # it is not ready to submit. next; } # The submission number (while often 1) is used for things below my $submission_number = $program_submission->CurrentSubmissionNumber() || 1; # Pull Metadata regarding whether we've tried to submit this via QPP # (only saved for Quality currently, so attached to the submission module) my $qpp_submission_metadata = {}; my @qpp_submission_metadata_keys = @{$submission_module->GetQPPSubmissionMetaDataKeys() || []}; my $submit_category_key = uc($mips_component)."_COMPOSITESCORE_OVERRIDE"; push (@qpp_submission_metadata_keys, $submit_category_key); if (@qpp_submission_metadata_keys) { $qpp_submission_metadata = $program_submission->GetMetaData({ SUBMISSIONNUMBER => $submission_number, KEYS => \@qpp_submission_metadata_keys, }); } my $submit_category_data = 1; #submit data if ( composite score >= threshold ) OR user has opted For it my @p4p_program_ids = ($p4p_program_id); # Pull the composite performance score from CMS my @performance_score_data = @{Clinical::P4P::Persistence::ProgramSubmission::GetPerformanceScores($dbh, { # In the future, we may want to pass this list of program ids through from the front # end, once the MIPS Dashboard will need to represent 2017 or 2018 P4PPROGRAMIDS => \@p4p_program_ids, # Limit to just this provider/TIN FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, })}; my $composite_score ; if (scalar @performance_score_data > 0) { $composite_score = $performance_score_data[0]->{COMPOSITESCORE}; } my $programs = Clinical::P4P::Utils::GetProgramInfo($dbh); my $programinfo = $programs->{$p4p_program_id}; $submit_category_data = ($programinfo->{BEGINSNAPSHOTTINGYN} eq 'Y') ? 0 : 1; $submission_blocking_reason = ($programinfo->{BEGINSNAPSHOTTINGYN} eq 'Y') ? 'COMPOSITE_SCORE_THRESHOLD' : ''; if( (defined $composite_score && $composite_score >= $composite_score_threshold ) || (defined $qpp_submission_metadata->{$submit_category_key} && $qpp_submission_metadata->{$submit_category_key} eq 'Y' ) ){ $submit_category_data = 1; $submission_blocking_reason = ''; } if(! $eligibilitycheckcalled) { $eligibilitycheckcalled = 1; $mipseligibilitystatus = _GetEligibilityStatus($dbh, { YEAR => $year, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, }); my @submissioneligiblestatuses = ('Eligible','Opted-in','QP','MIPS APM', 'Ineligible', 'Unknown'); if (defined Athena::Conf::AthenaNet::ExternalServices('qpp')->{submission_eligible_statuses}) { @submissioneligiblestatuses = @{Athena::Conf::AthenaNet::ExternalServices('qpp')->{submission_eligible_statuses}}; } if( InList($mipseligibilitystatus , @submissioneligiblestatuses)) { # eligible for submission $iseligibleforsubmission = 1; } elsif($mipseligibilitystatus eq 'Opt-in') { my $metadata = $program_submission->GetMetaData ({ KEYS => [qw( OPTINSTATUS )], }); if($metadata->{OPTINSTATUS} eq 'true') { $iseligibleforsubmission = 1; } else { $iseligibleforsubmission = 0; } } else { $iseligibleforsubmission = 0; } if($mipseligibilitystatus eq 'Unknown') { #if eligibility status is Unknow only CQM's should not be submitted #the error message should showup only if we are submitting any CQM's my $p4p_program_id_quality = Athena::P4P::App::PerformanceData::GetMIPSProgram($dbh, { CATEGORY => 'Quality', YEAR => $year, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); $p4p_program_id_quality = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $p4p_program_id_quality, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => 'Quality', YEAR => $year, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', }); my $program_submission_quality = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $p4p_program_id_quality, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); if (! $program_submission_quality->NoDenormalizedData()) { my $attestationmodule = Clinical::P4P::Utils::GetProgramModule($dbh, { P4PPROGRAMID => $p4p_program_id_quality, TYPE => 'Attestation', }); my $reporting_period = $program_submission_quality->ReportingPeriod(); my $attestation_deadline = $attestationmodule->GetAttestationDeadline({ REPORTINGPERIOD => $reporting_period, }); $return_status_quality = $attestationmodule->GetAttestationRequiredStatus($dbh, { SUBMISSIONOBJECT => $program_submission_quality, ATTESTATIONDEADLINE => $attestation_deadline, }); } } } #if the return status is nonedtoattest that means we are not submitting any CQM's and hence error message need not be shown #any other status other than noneedtoattest we need to show the unknown error message because we are submitting CQM's in those cases if($mipseligibilitystatus eq 'Unknown' && $return_status_quality ne undef && $return_status_quality ne 'NoNeedToAttest' ) { $submission_blocking_reason = 'UNKNOWN_ELIGIBILITY_STATUS'; } if(!$iseligibleforsubmission && ($mipseligibilitystatus eq 'Error in NPI' || $mipseligibilitystatus eq 'Ineligible' || $mipseligibilitystatus eq 'Opt-in') ) { if ($mipseligibilitystatus eq 'Opt-in') { $submission_blocking_reason = 'NOT_OPTED_IN_ELIGIBILITY_STATUS'; } elsif ($mipseligibilitystatus eq 'Error in NPI') { $submission_blocking_reason = 'NPI_TIN_MISMATCH_ELIGIBILITY_STATUS'; } elsif ($mipseligibilitystatus eq 'Ineligible') { $submission_blocking_reason = 'INELIGIBLE_ELIGIBILITY_STATUS'; } } $program_submission->UpdateMetaData({ USERNAME => $Global::session{USERNAME}, DATA => { SUBMISSION_BLOCKING_REASON => $submission_blocking_reason, }, }); # Evaluate whether this provider has been blacklisted from submission. my $is_blacklisted = Clinical::P4P::Blacklist::IsBlacklisted($dbh, { P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, SUBMISSIONNUMBER => $submission_number, })->{BLACKLISTED}; # Determine which components have met minimum reporting requirements. my $satisfied_components = Athena::P4P::App::PerformanceData::GetSatisfiedComponents($dbh, { PROGRAMSUBMISSION => $program_submission, ISMVP => $args->{ISMVP}, }); # Get the status of this program my $status_hash = $program_submission->GetStatusBySubmissionNumber({ SUBMISSIONNUMBER => $submission_number, }) || {}; my $program_status = $status_hash->{STATUS}; my $qpp_conf = Athena::Conf::AthenaNet::ExternalServices('qpp'); my $start_submission = exists($qpp_conf->{munge_pii}) && $qpp_conf->{munge_pii} eq '1' ? 0 : 1; foreach my $key (@satisfied_component_keys) { # Evaluate if this component satisfies status & metadata submission criteria my $in_acceptable_status = _InAcceptableStatusForSubmission($dbh, { COMPONENT => $key, PROGRAMSTATUS => $program_status, QPPSUBMISSIONMETADATA => $qpp_submission_metadata, REQUIREDSTATUSCLASS => $component_key_attestation_status_required->{$key}, }); if($key eq 'QUALITY_REGISTRY' && $mipseligibilitystatus eq 'Unknown') { $eligibile_to_submit = 0; } elsif(!$iseligibleforsubmission){ $eligibile_to_submit = 0; } # Put everything together to evaluate if this program component is ready for submission $return_structure{$key} = ( !$is_blacklisted && $in_acceptable_status && $satisfied_components->{$key} && $submit_category_data && $eligibile_to_submit && $start_submission ) ? '1' : ''; } } return \%return_structure; } ################################################################################ # _InAcceptableStatusForSubmission # # Description: # Evaluates whether the given component is eligible to be submitted based on # the status of the program. This looks at a few criteria: # 1) The program status is not SUBMITTED # 2) If QPP Metadata exists for this component, then that it doesn't indicate # that there has been a successful QPP submission already # 3) That the program status has attained a minimally-required status # as necessitated for this component. # # Parameters: # Required: # # COMPONENT - string - the key of a component (see GetSatisfiedComponentKeys) # # PROGRAMSTATUS - string - the status of the current program submission # # QPPSUBMISSIONMETADATA - hashref of data returned by GetMetaData representing # QPP Submission information for this component # # REQUIREDSTATUSCLASS - string - the minimally-required status needed for # submission by this component # # Returns: # Boolean. True represents that this component is eligible for submission based # on the passed-in information; false represents that this component is ineligible # for submission. ################################################################################ sub _InAcceptableStatusForSubmission { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( COMPONENT PROGRAMSTATUS QPPSUBMISSIONMETADATA REQUIREDSTATUSCLASS )], [qw( )], ); my $component = $args->{COMPONENT}; my $program_status = $args->{PROGRAMSTATUS}; my $qpp_submission_metadata = $args->{QPPSUBMISSIONMETADATA}; my $required_status_class = $args->{REQUIREDSTATUSCLASS}; my $in_acceptable_status; # If the program was already SUBMITTED, it should never be submitted again, as # this is a final state if ($program_status ne 'SUBMITTED') { my $metadata_key = $component . '_SUBMISSION'; # If the whole program isn't fully SUBMITTED, but there is metadata supporting # that this particular submission method was successful, the data should not # be submitted again for this submission method. That is, continue evaluating # for submission readiness if there is no metadata or if the metadata shows # that submission was not successful. if ($qpp_submission_metadata->{ $metadata_key } ne 'SUCCESS') { # If we've gotten this far, then we know the program and/or submission method # has not yet been submitted. # See if the program is in a minimally-acceptable status (depending on the # program and submission method, that it was snapshotted and/or attested) $in_acceptable_status = $Clinical::P4P::Attestation::AttestationStatuses{$required_status_class}->{$program_status}; } } return $in_acceptable_status; } ################################################################################ # GetSatisfiedComponents # # Description: # Given a program submission object, determines which components of MIPS # have met minimum reporting requirements. The computation of whether a component # has met minimum requirements only happens at snapshotting, so the results will # not be useful for a component that has not snapshotted. # # Parameters: # Required: # PROGRAMSUBMISSION - a Clinical::P4P::Persistence::ProgramSubmission object # # Returns: # Hashref, where the keys are uppercased concatenations of the MIPS component name # and an underscore and the submission method (e.g., QUALITY_EHR) as returned by # GetSatisfiedComponentKeys. # The values are Boolean, representing whether or not that component # (and submission method) has met minimum reporting requirements. ################################################################################ sub GetSatisfiedComponents { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( PROGRAMSUBMISSION )], [qw( ISMVP )], ); my $program_submission = $args->{PROGRAMSUBMISSION}; my $is_mvp = $args->{ISMVP} || 0; my $mips_component = $is_mvp ? Athena::P4P::App::PerformanceData::GetMVPCategory($dbh, $program_submission->P4PProgramID()) : Athena::P4P::App::PerformanceData::GetMIPSCategory($dbh, $program_submission->P4PProgramID()); Athena::Util::Assert::Assert( $mips_component, 'The supplied program submission does not represent a valid MIPS component', ); # Get all expected keys for this MIPS component my @result_keys = @{ Athena::P4P::App::PerformanceData::GetSatisfiedComponentKeys({ MIPSCOMPONENT => $mips_component }) }; # Use the metadata to evaluate if all minimum reporting requirements were met for the program. # The following code makes an assumption that there is only one piece of metadata # being stored for this key. The structure of $metadata may be unexpected if in # the future it returns more information under the exact same keys. my $metadata = $program_submission->GetMetaData({ KEYS => \@result_keys, }) || {}; # The following code makes an assumption that all programs will continue to store # metadata indicating that minimum reporting requirements have been met. This is # definitely true for Quality, but would need to be verified for future programs (ACI, IA) my %return_structure; foreach my $key (@result_keys) { my $met_minimum_requirements = $metadata->{$key} && $metadata->{$key} eq 'MinRequirementsMet'; $return_structure{$key} = !!$met_minimum_requirements; } return \%return_structure; } ################################################################################ # GetSatisfiedComponentKeys # # Description: # Given a MIPS component, returns the submission data keys that store whether # the component has met minimum reporting requirements. # # For components that have only a single submission method, the key will just # be the MIPS component name. The Quality program throws a wrench into simply using # the component name, since we need to know about how to submit EHR and REGISTRY # separately. We'll create keys that are a concatenation of the mips component name # and the submission method, all upper-cased to avoid confusion. # # Parameters: # Required: # MIPSCOMPONENT String MIPS component for which to retreive keys. Must be # a key in SUBMISSION_METHODS_FOR_PROGRAM(). # Optional: # None. # # Returns: # Arrayref of strings, each string being a key in the program's submission data # that indicates whether minimum reporting requirements were met for that component # and submission method. ################################################################################ sub GetSatisfiedComponentKeys { my ($args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( MIPSCOMPONENT )], [qw( )], ); my $mips_component = $args->{MIPSCOMPONENT}; Athena::Util::Assert::Assert( exists SUBMISSION_METHODS_FOR_PROGRAM()->{$mips_component}, 'MIPSCOMPONENT must be a valid key in SUBMISSION_METHODS_FOR_PROGRAM()', ); my @submission_methods = @{SUBMISSION_METHODS_FOR_PROGRAM()->{$mips_component} || []}; my @result_keys = @submission_methods ? map {uc($mips_component . "_" . $_)} @submission_methods : ($mips_component); return \@result_keys; } ########################################################################################## # GetMIPSProgramsByCategory # # Description: # MIPS programs are organized into triplets - a Quality Program, an ACI program, and # an IA program. Given one program in the triplet, this returns a HashRef that specifies # all three and their categories. # # Parameters: # Required: # P4PPROGRAMID Integer Id of one of the programs in the triplet. # Optional: # None. # # Return Value: # HashRef of the form: # <category, e.g. 'Quality' or 'ACI'> => <p4p program id> ########################################################################################## sub GetMIPSProgramsByCategory { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMID )], [qw( ISMVP )], ); my $p4p_program_id = $args->{P4PPROGRAMID}; my $is_mvp = $args->{ISMVP} || 0; my $reporting_period = Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => $p4p_program_id, }); my $year = $reporting_period->ProgramYear(); my $is_subgrp_toggleon = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'CLQI_5651_MVP_SUBGRP', }) eq 'ON'; my $is_subgroup; my $subgroup_programids = Clinical::P4P::Utils::GetSubGroupMap($dbh)->{SUBGROUPPROGRAMIDS}{$year}; if(Athena::Util::List::InList($p4p_program_id, @$subgroup_programids) && $is_subgrp_toggleon){ $is_subgroup = 1; } my $entity_type = $is_mvp ? GetMVPEntityType($dbh, $p4p_program_id) : GetMIPSEntityType($dbh, $p4p_program_id); if (! $entity_type) { # If we cannot derive an entity type, we were given an invalid MIPS Program. # Therefore we should return nothing. return {}; } my $aci_pi_category = 'PI'; if ( $year == 2017 ) { $aci_pi_category = 'ACI'; } return { $aci_pi_category => $is_subgroup ? Athena::P4P::App::PerformanceData::GetSubGroupProgramByCategory($dbh,{ CATEGORY => 'PI', }) : Athena::P4P::App::PerformanceData::GetMIPSProgram($dbh, { CATEGORY => $aci_pi_category, YEAR => $year, ENTITYTYPE => $entity_type, ISMVP => $is_mvp, }), Quality => $is_subgroup ? Athena::P4P::App::PerformanceData::GetSubGroupProgramByCategory($dbh,{ CATEGORY => 'Quality', }) : Athena::P4P::App::PerformanceData::GetMIPSProgram($dbh, { CATEGORY => 'Quality', YEAR => $year, ENTITYTYPE => $entity_type, ISMVP => $is_mvp, }), IA => $is_subgroup ? Athena::P4P::App::PerformanceData::GetSubGroupProgramByCategory($dbh,{ CATEGORY => 'IA', }) : Athena::P4P::App::PerformanceData::GetMIPSProgram($dbh, { CATEGORY => 'IA', YEAR => $year, ENTITYTYPE => $entity_type, ISMVP => $is_mvp, }), }; } ########################################################################################## # _GetMIPSCategoryMap # # Description: # Returns a hashref that maps each MIPS program ID to a MIPS category (Quality, IA, or ACI) # # Parameters: # None. # # Return Value: # Hashref of entries of the form P4PROGRAMID => Category, e.g., # { # 3119 => "ACI", # 3152 => "Quality", # 3205 => "IA", # 3791 => "ACI", # 3796 => "IA", # 3797 => "Quality" # } ########################################################################################## sub _GetMIPSCategoryMap { my ($dbh, $is_mvp) = @_; my $years = GetMIPSProgramYearList($dbh, $is_mvp); my %category_map = (); foreach my $year (@$years) { my @categories = @{ _AllCategoriesForYear( { YEAR => $year, ISMVP => $is_mvp, }) }; foreach my $category (@categories) { my @entity_types = @{ _AllEntityTypesForYearAndCategory({ YEAR => $year, CATEGORY => $category, ISMVP => $is_mvp }) }; foreach my $entity_type (@entity_types) { my $program_id = _GetProgramIDForYearCategoryEntityType($dbh, { YEAR => $year, CATEGORY => $category, ENTITYTYPE => $entity_type, ISMVP => $is_mvp }); foreach my $programid (@$program_id) { $category_map{$programid} = $category if defined $programid; } } } } my $subgroup_categorymap = Clinical::P4P::Utils::GetSubGroupMap($dbh); foreach my $programid (keys %{$subgroup_categorymap->{CATEGORYMAP}}){ $category_map{$programid} = $subgroup_categorymap->{CATEGORYMAP}{$programid}; } return \%category_map; } # Save the MIPS category map for use in GetMIPSCategory. my $mips_category_map = \&_GetMIPSCategoryMap; ########################################################################################## # GetMIPSCategory # # Description: # Returns the MIPS category (Quality, IA, or ACI) for a given MIPS program # # Parameters: # Required: # $dbh Database handle # $p4p_program_id Integer P4P program ID # # Optional: # None. # # Return Value: # The MIPS category that corresponds to the program ID. # For example, GetMIPSCategory($dbh, 3152) returns 'Quality'. ########################################################################################## sub GetMIPSCategory { my ($dbh, $p4p_program_id) = @_; Athena::Util::Assert::Assert(defined $p4p_program_id, 'The p4p_program_id argument is required.'); return $mips_category_map->($dbh)->{$p4p_program_id}; } ########################################################################################## # GetMVPCategory # # Description: # Returns the MVP category (Quality, IA, or ACI) for a given MVP program # # Parameters: # Required: # $dbh Database handle # $p4p_program_id Integer P4P program ID # # Optional: # None. # # Return Value: # The MVP category that corresponds to the program ID. # For example, GetMVPCategory($dbh, 3152) returns 'Quality'. ########################################################################################## sub GetMVPCategory { my ($dbh, $p4p_program_id) = @_; my $is_mvp = 1; Athena::Util::Assert::Assert(defined $p4p_program_id, 'The p4p_program_id argument is required.'); return $mips_category_map->($dbh, $is_mvp)->{$p4p_program_id}; } ########################################################################################## # _GetMIPSEntityTypeMap # # Description: # Returns a hashref that maps each MIPS program ID to an entity type (INDIVIDUAL or GROUP) # # Parameters: # None. # # Return Value: # Hashref of entries of the form P4PROGRAMID => Category, e.g., # { # 3119 => "INDIVIDUAL", # 3152 => "INDIVIDUAL", # -3152 => "GROUP", # } ########################################################################################## sub _GetMIPSEntityTypeMap { my ($dbh, $is_mvp) = @_; my $years = GetMIPSProgramYearList($dbh, $is_mvp); my %entity_map = (); foreach my $year (@$years) { my @categories = @{ _AllCategoriesForYear({ YEAR => $year, ISMVP => $is_mvp, }) }; foreach my $category (@categories) { my @entity_types = @{ _AllEntityTypesForYearAndCategory({ YEAR => $year, CATEGORY => $category, ISMVP => $is_mvp }) }; foreach my $entity_type (@entity_types) { my $program_id = _GetProgramIDForYearCategoryEntityType($dbh, { YEAR => $year, CATEGORY => $category, ENTITYTYPE => $entity_type, ISMVP => $is_mvp }); foreach my $programid (@$program_id) { $entity_map{$programid} = $entity_type if defined $programid; } } } } my $subgroup_entitymap = Clinical::P4P::Utils::GetSubGroupMap($dbh); foreach my $programid (keys %{$subgroup_entitymap->{ENTITYMAP}}){ $entity_map{$programid} = $subgroup_entitymap->{ENTITYMAP}{$programid}; } return \%entity_map; } # Save the MIPS entity type map for use in GetMIPSEntityType. my $mips_entity_map = \&_GetMIPSEntityTypeMap; ########################################################################################## # GetMIPSEntityType # # Description: # Returns the MIPS entity type (INDIVIDUAL or GROUP) for a given MIPS program # # Parameters: # Required: # $dbh Database handle # $p4p_program_id Integer P4P program ID # # Optional: # None. # # Return Value: # The MIPS entity type that corresponds to the program ID. # For example, GetMIPSCategory($dbh, 3152) returns 'INDIVIDUAL'. ########################################################################################## sub GetMIPSEntityType { my ($dbh, $p4p_program_id) = @_; Athena::Util::Assert::Assert(defined $p4p_program_id, 'The p4p_program_id argument is required.'); return $mips_entity_map->($dbh)->{$p4p_program_id}; } ########################################################################################## # GetMVPEntityType # # Description: # Returns the MIPS entity type (INDIVIDUAL or GROUP) for a given MVP program # # Parameters: # Required: # $dbh Database handle # $p4p_program_id Integer P4P program ID # # Optional: # None. # # Return Value: # The MVP entity type that corresponds to the program ID. # For example, GetMVPCategory($dbh, 3152) returns 'INDIVIDUAL'. ########################################################################################## sub GetMVPEntityType { my ($dbh, $p4p_program_id) = @_; my $is_mvp = 1; Athena::Util::Assert::Assert(defined $p4p_program_id, 'The p4p_program_id argument is required.'); return $mips_entity_map->($dbh, $is_mvp)->{$p4p_program_id}; } ########################################################################################## # GetMIPSProgramList # # Description: # Returns all the MIPS programs filtered by various criteria : # - MIPS category (e.g., Quality, ACI, or IA) # - Provider IDs # - Federal ID numbers (TINs) # - Program years # - Entity type # # Parameters: # Required: # None. # # Optional: # CATEGORY String The MIPS category (Quality, ACI, or IA) # # FEDERALIDNUMBERS Arrayref The TINs whose MIPS programs should be returned # and/or # PROVIDERIDS Arrayref The providers whose MIPS programs should be returned # # YEARS Arrayref The MIPS program years that apply # ENTITYTYPES Arrayref A list containing INDIVIDUAL, GROUP, or both. # # Return Value: # Arrayref of P4P program IDs for the MIPS programs selected. ########################################################################################## sub GetMIPSProgramList { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [], [qw( CATEGORY PROVIDERIDS FEDERALIDNUMBERS YEARS ENTITYTYPES ISMVP )], ); my $category_arg = $args->{CATEGORY}; Athena::Util::Assert::Assert(! defined $category_arg || _IsMIPSCategory($category_arg), "$category_arg is not a valid MIPS category."); my $provider_ids = $args->{PROVIDERIDS}; my $federal_id_numbers = $args->{FEDERALIDNUMBERS}; my $is_mvp = $args->{ISMVP} || 0; my $valid_program_years = GetMIPSProgramYearList($dbh, $is_mvp); my $years = $args->{YEARS} // $valid_program_years; $years = $valid_program_years if scalar @$years == 0; Athena::Util::Assert::Assert(ref $years eq 'ARRAY', 'The YEARS argument, if provided, must either be undef or an arrayref.'); my @entity_types_arg = @{$args->{ENTITYTYPES} // []}; my %program_ids = (); foreach my $year (@$years) { _AssertValidYear($year); my @categories = defined $category_arg ? ( $category_arg ) : @{_AllCategoriesForYear({ YEAR => $year, ISMVP => $is_mvp, })}; foreach my $category (@categories) { my @entity_types = scalar @entity_types_arg ? @entity_types_arg : @{ _GetApplicableEntityTypes($dbh, { YEAR => $year, CATEGORY => $category, PROVIDERIDS => $provider_ids, FEDERALIDNUMBERS => $federal_id_numbers, ISMVP => $is_mvp, }) }; foreach my $entity_type (@entity_types) { _AssertValidEntityType($entity_type); my $program_id = _GetProgramIDForYearCategoryEntityType($dbh, { YEAR => $year, CATEGORY => $category, ENTITYTYPE => $entity_type, ISMVP => $is_mvp, }); foreach my $programid (@$program_id) { $program_ids{$programid} = 1 if defined $programid; } } } } return [ sort { $a <=> $b } keys %program_ids ]; } ########################################################################################## # GetSubGroupProgramByCategory # # Description: # Returns the MIPS program associated with a MIPS category (e.g., Quality, ACI, # or IA). # ########################################################################################## sub GetSubGroupProgramByCategory { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( CATEGORY )], ); my $category = $args->{CATEGORY}; my @programids_bycategory; my $subgroup_category_map = Clinical::P4P::Utils::GetSubGroupMap($dbh); foreach my $programid (sort keys %{$subgroup_category_map->{CATEGORYMAP}}){ if($subgroup_category_map->{CATEGORYMAP}{$programid} eq $category){ push(@programids_bycategory,$programid); } } return \@programids_bycategory; } ########################################################################################## # GetMIPSProgram # # Description: # Returns the MIPS program associated with a MIPS category (e.g., Quality, ACI, # or IA). # # Parameters: # Required: # CATEGORY String The MIPS category (Quality, ACI, or IA) # YEAR Integer The program year that applies # # Optional: # FEDERALIDNUMBER String The TIN whose MIPS programs should be returned # and/or # PROVIDERID Integer The provider whose MIPS programs should be returned # # ENTITYTYPE String INDIVIDUAL or GROUP. Defaults to INDIVIDUAL if neither # PROVIDERID or FEDERALIDNUMBER is provided. # # Return Value: # The P4P program ID for the MIPS program ########################################################################################## sub GetMIPSProgram { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( CATEGORY YEAR )], [qw( PROVIDERID FEDERALIDNUMBER ENTITYTYPE ISMVP )], ); my $category = $args->{CATEGORY}; my $provider_id = $args->{PROVIDERID}; my $provider_ids = defined $provider_id ? [ $provider_id ] : []; my $federal_id_number = $args->{FEDERALIDNUMBER}; my $federal_id_numbers = defined $federal_id_number ? [ $federal_id_number ] : []; my $is_mvp = $args->{ISMVP} || 0; my $entity_type; if (defined $provider_id || defined $federal_id_number) { $entity_type = $args->{ENTITYTYPE}; } else { $entity_type = $args->{ENTITYTYPE} // 'INDIVIDUAL'; } _AssertValidEntityType($entity_type) if defined $entity_type; my $entity_types = defined $entity_type ? [ $entity_type ] : []; my $year = $args->{YEAR}; _AssertValidYear($year); my $years = defined $year ? [ $year ] : []; my $program_ids = GetMIPSProgramList($dbh, { CATEGORY => $category, PROVIDERIDS => $provider_ids, FEDERALIDNUMBERS => $federal_id_numbers, YEARS => $years, ENTITYTYPES => $entity_types, ISMVP => $is_mvp, }); return $program_ids; } # Utility functions # Is the category name a valid MIPS category? sub _IsMIPSCategory { my ($category) = @_; return MIPS_CATEGORIES()->{$category}; } # Is the year valid? sub _AssertValidYear { my ($year) = @_; # Assert does not fire correctly if the boolean regex match expression below is # provided as the first argument. Assigning it to another variable first is a # workaround. my $valid_year = $year =~ /^\d{4}$/; Athena::Util::Assert::Assert($valid_year, "The YEAR parameter '$year' is not in YYYY format."); return; } # Is the entity type valid? sub _AssertValidEntityType { my ($entity_type) = @_; Athena::Util::Assert::Assert($entity_type eq 'INDIVIDUAL' || $entity_type eq 'GROUP', "The entity type '$entity_type' is not 'INDIVIDUAL' or 'GROUP'."); return; } # Get all categories for a year. sub _AllCategoriesForYear { my ($args) = @_; Athena::Util::Assert::AssertFields( $args, [ qw( YEAR ), ], [ qw( ISMVP ) ], ); my $year = $args->{YEAR}; my $is_mvp = $args->{ISMVP}; my $year_data; if($is_mvp) { $year_data = _MVP_PROGRAM_DATA()->{$year}; } else { $year_data = _MIPS_PROGRAM_DATA()->{$year}; } return [ keys %$year_data ]; } # Get all supported categories for a year. sub _SupportedCategoriesForYear { my ($args) = @_; Athena::Util::Assert::AssertFields( $args, [ qw( YEAR ), ], [ qw( ISMVP ) ], ); my $year = $args->{YEAR}; my $is_mvp = $args->{ISMVP}; return _AllCategoriesForYear({ YEAR => $year, ISMVP => $is_mvp, }); } # Get all entity types for a year and category. sub _AllEntityTypesForYearAndCategory { my ($args) = @_; my $year = $args->{YEAR}; my $category = $args->{CATEGORY}; my $is_mvp = $args->{ISMVP}; return if ! defined $year || ! defined $category; my $entity_type_data = _GetEntityTypeData({ YEAR => $year, CATEGORY => $category, ISMVP => $is_mvp }); return [] if ! defined $entity_type_data; return [ keys %$entity_type_data ]; } # Gets the entity type data for a year and category. sub _GetEntityTypeData { my ($args) = @_; my $year = $args->{YEAR}; my $category = $args->{CATEGORY}; my $is_mvp = $args->{ISMVP}; return if ! defined $year || ! defined $category; my $year_data; if($is_mvp) { $year_data = _MVP_PROGRAM_DATA()->{$year}; } else { $year_data = _MIPS_PROGRAM_DATA()->{$year}; } return if ! defined $year_data; my $category_data = $year_data->{$category}; return if ! defined $category_data; my $entity_type_data = $category_data->{ENTITYTYPE}; return if ! defined $entity_type_data; return $entity_type_data; } # Get the program ID for the year, category, and entity type. Note that the FULLYEAR reporting period is hardcoded for now. sub _GetProgramIDForYearCategoryEntityType { my ($dbh, $args) = @_; my $year = $args->{YEAR}; my $category = $args->{CATEGORY}; my $entity_type = $args->{ENTITYTYPE}; my $is_mvp = $args->{ISMVP}; return if ! defined $year || ! defined $category || ! defined $entity_type; if($year == 2019 && $category eq 'Quality') { my $mips_quality_programid = _GetMIPSQMassQualityProgramIds({ CATEGORY => $category, ENTITYTYPE => $entity_type, }); return [$mips_quality_programid]; } my $entity_type_data = _GetEntityTypeData({ YEAR => $year, CATEGORY => $category, ISMVP => $is_mvp }); return if ! defined $entity_type_data; my $entity_type_instance = $entity_type_data->{$entity_type}; return if ! defined $entity_type_instance; my $full_year_data = $entity_type_instance->{FULLYEAR}; my $ninety_days = $entity_type_instance->{NINETYDAYS}; return if ! defined $full_year_data && ! $ninety_days; my @ninetydayslist = split(m/[,]/, $ninety_days->{P4PPROGRAMID}); my @programids; if($is_mvp) { my @fullyeardatalist = split(m/[,]/, $full_year_data->{P4PPROGRAMID}); @programids = (@fullyeardatalist, @ninetydayslist ); } else { @programids = ($full_year_data->{P4PPROGRAMID}, @ninetydayslist ); } return \@programids; } # Returns the entity types that apply to the parameters. sub _GetApplicableEntityTypes { my ($dbh, $args) = @_; my @federal_id_numbers = @{ $args->{FEDERALIDNUMBERS} // [] }; my @provider_ids = @{ $args->{PROVIDERIDS} // [] }; my $is_mvp = $args->{ISMVP} || 0; my $entity_types; if (@provider_ids) { $entity_types = [ 'INDIVIDUAL']; } elsif (@federal_id_numbers) { $entity_types = [ 'GROUP' ]; } else { $entity_types = _AllEntityTypesForYearAndCategory({ YEAR => $args->{YEAR}, CATEGORY => $args->{CATEGORY}, ISMVP => $is_mvp, }); } return $entity_types; } ################################################################################ # GetMIPSSubmissionStatuses # # Description: # Gets the submission status for each MIPS component for a provider or TIN, as # well as whether each component is in a status where it can be unsnapshotted. # # Also returns a flag indicating whether the supplied username has permission # to undo snapshots. If the user does not have said permission, the hash of # submission statuses will be empty. # # Parameters: # Required: # FEDERALIDNUMBER The TIN whose MIPS statuses should be returned # and/or # PROVIDERID The provider whose MIPS statuses should be returned # # P4PPROGRAMID The id of one of the MIPS programs in the year for # which we are fetching data # USERNAME The username whose unsnapshot permission should be checked # # Returns: # Hashref with the following keys: # # COMPONENTS A hashref of the form returned by _GetMIPSComponentStatuses. # Will by an empty hashref if the user does not have permission # to undo snapshots. # UNDOSNAPSHOTPERMISSION True if the user has permissions to undo snapshots ################################################################################ sub GetMIPSSubmissionStatuses { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [ { ONEPLUS => [qw( FEDERALIDNUMBER PROVIDERID )], }, 'P4PPROGRAMID', 'USERNAME', ], [], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $provider_id = $args->{PROVIDERID}; my $username = $args->{USERNAME}; my $undo_snapshot_permission = AthenaSecurity::ResourceSecure($dbh, { RESOURCE => 'CLINICALSP4PCONTENT', USERNAME => $username, }); my $is_mips_aci_nada_enabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'CLQP_MIPS_ACI_NADA_2018', }) eq 'ON'; if ($undo_snapshot_permission) { my $component_statuses = Athena::P4P::App::PerformanceData::_GetMIPSComponentStatuses($dbh, { FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, }); if ($is_mips_aci_nada_enabled) { my $legacy_statuses = Athena::P4P::App::PerformanceData::HandleMIPSLegacyData($dbh, { FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, }); return { COMPONENTS => $component_statuses, LEGACYCOMPONENTS => $legacy_statuses, UNDOSNAPSHOTPERMISSION => 1, }; } else { return { COMPONENTS => $component_statuses, UNDOSNAPSHOTPERMISSION => 1, }; } } else { if ($is_mips_aci_nada_enabled) { return { COMPONENTS => {}, LEGACYCOMPONENTS => {}, UNDOSNAPSHOTPERMISSION => 0, }; } else { return { COMPONENTS => {}, UNDOSNAPSHOTPERMISSION => 0, }; } } } ################################################################################ # _GetMIPSComponentStatuses # # Description: # Gets the submission status for each MIPS component for a provider or TIN, # as well as whether each component is in a status where it can be unsnapshotted. # # Parameters: # Required: # FEDERALIDNUMBER The TIN whose MIPS statuses should be returned # and/or # PROVIDERID The provider whose MIPS statuses should be returned # # P4PPROGRAMID The id of one of the MIPS programs in the year # for which we are fetching data # # Returns: # Hashref where each key is a MIPS component (Quality, IA, ACI) # and each value is a hashref of the form returned by _GetMIPSComponentStatus ################################################################################ sub _GetMIPSComponentStatuses { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [ { ONEPLUS => [qw( FEDERALIDNUMBER PROVIDERID )], }, 'P4PPROGRAMID', ], [], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $program_id_by_category = Athena::P4P::App::PerformanceData::GetMIPSProgramsByCategory($dbh, { P4PPROGRAMID => $p4p_program_id, }); my %return_structure; my $programid; foreach my $category (sort keys %$program_id_by_category) { $programid = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $program_id_by_category->{$category}, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => $category, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', }); $return_structure{$category} = Athena::P4P::App::PerformanceData::_GetMIPSComponentStatus($dbh, { FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $programid || $program_id_by_category->{$category}, PROVIDERID => $provider_id, }); } return \%return_structure; } ################################################################################ # _GetMIPSComponentStatus # # Description: # Gets the submission status for a single MIPS component for a provider/TIN, as # well as whether the status is such that it can be unsnapshotted. # # Parameters: # Required: # FEDERALIDNUMBER The TIN whose status should be returned # and/or # PROVIDERID The provider whose status should be returned # # P4PPROGRAMID The id of the p4p program for which to get the status # # Returns: # Hashref of the form # { # P4PPROGRAMID => The id of the p4p program (same as input parameter) # STATUS => The status of the provider or TIN's submission for # this component # SUBMISSIONNUMBER => The current submission number of the submission # ALLOWUNDOSNAPSHOT => True if the status is one where unsnapshotting is # allowed # } ################################################################################ sub _GetMIPSComponentStatus { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [ { ONEPLUS => [qw( FEDERALIDNUMBER PROVIDERID )], }, 'P4PPROGRAMID', ], [], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $program_submission = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); my $submission_number = $program_submission->CurrentSubmissionNumber() || 1; my $status_hash = $program_submission->GetStatusBySubmissionNumber({ SUBMISSIONNUMBER => $submission_number, }) || {}; my $status = $status_hash->{STATUS}; my $allow_undo = $Clinical::P4P::Attestation::AttestationStatuses{SNAPSHOTTED}->{ $status } ? 1 : 0; my $error; my $qpp_submission; my $isrollouttoggleon = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'CORRECT_SUBMISSIONSCORE_ON_MIPSDB', }) eq 'ON'; my $isblacklistrulerollouttoggleon = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'QPSU_MIPS_DB_BlCKLIST_OPTIN', }) eq 'ON'; my $blacklistrulenameandid; if($isblacklistrulerollouttoggleon) { my $is_blacklisted = Clinical::P4P::Blacklist::IsBlacklisted($dbh, { P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, SUBMISSIONNUMBER => $submission_number, }); my @blacklistruleids; my %p4pblacklistrulenamesbyid; my @p4pblacklistrulenames; # if blacklisted get the details if($is_blacklisted->{BLACKLISTED}) { if($is_blacklisted->{BLACKLISTRULEID}) { @blacklistruleids = @{$is_blacklisted->{BLACKLISTRULEID}}; %p4pblacklistrulenamesbyid = SQL::Select->new( )->Select( "p4pblacklistrule.id", "p4pblacklistrule.name", )->From( "p4pblacklistrule", )->Where( "p4pblacklistrule.deleted is null", ['p4pblacklistrule.id in (??)', \@blacklistruleids], )->ColumnValues($dbh); foreach my $blacklistruleid (@blacklistruleids) { my $blacklistrulename = $p4pblacklistrulenamesbyid{$blacklistruleid}; $blacklistrulename = $blacklistrulename . ' (' . $blacklistruleid . ')'; push @p4pblacklistrulenames, $blacklistrulename; } } if($is_blacklisted->{DEFAULTTOBLACKLIST}) { push @p4pblacklistrulenames, 'Default To Blacklist is true'; } $blacklistrulenameandid = join( ',', @p4pblacklistrulenames ); } } if($isrollouttoggleon) { my @qpp_submissions = Athena::P4P::Persistence::QPP::GetQPPSubmissions($dbh, { PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $p4p_program_id, SUBMISSIONNUMBER => $submission_number, REQUESTTYPE => 'FINALSCORING', STATUS => 'SUCCESS', }); $qpp_submission = $qpp_submissions[0]; my $error_message = $qpp_submission->{ERRORMESSAGE}; my $response = $qpp_submission->{RESPONSE}; if ($response ne undef) { my $parsed_response = Athena::P4P::Persistence::QPP::ParseQPPResponse({ RESPONSE => YAML::XS::Load($response), }); $error = !$parsed_response->{PARSEDCONTENT}->{data}->{score}->{value} ? $parsed_response->{PARSEDCONTENT}->{data}->{score}->{errors} : ''; } if($isblacklistrulerollouttoggleon) { #if there are no submission with FINALSCORING we want to check if there is a SUBMISSION request with opt-in error if(!@qpp_submissions) { @qpp_submissions = Athena::P4P::Persistence::QPP::GetQPPSubmissions($dbh, { PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $p4p_program_id, SUBMISSIONNUMBER => $submission_number, REQUESTTYPE => 'SUBMISSION', STATUS => 'FAILINVESTIGATE', RESPONSECODE => 422, }); #if a 422 error exists then check if it is not a validation error as we can have a SUBMISSION request with a validation error (preview env) and we don't want to show this if(@qpp_submissions && $qpp_submissions[0]->{ERRORMESSAGE} && index($qpp_submissions[0]->{ERRORMESSAGE},'ValidationError') == -1) { $qpp_submission = $qpp_submissions[0]; } } } } return { ALLOWUNDOSNAPSHOT => $allow_undo, P4PPROGRAMID => $p4p_program_id, STATUS => $status, ERRORMSG => $qpp_submission->{ERRORMESSAGE} ? $qpp_submission->{ERRORMESSAGE} : $error, SUBMISSIONNUMBER => $submission_number, BLACKLISTRULENAMEANDID => $blacklistrulenameandid ? $blacklistrulenameandid : undef, }; } ################################################################################ # GetMIPSLegacyStatuses # # Description: # Gets the legacy status for each MIPS component for a provider or TIN, as # well as whether each component is in a status where it can be unsnapshotted. # # # Parameters: # Required: # FEDERALIDNUMBER The TIN whose MIPS statuses should be returned # and/or # PROVIDERID The provider whose MIPS statuses should be returned # # P4PPROGRAMID The id of one of the MIPS programs in the year for # which we are fetching data # USERNAME The username whose unsnapshot permission should be checked # # Returns: # Hashref with the following keys: # # COMPONENTS A hashref of the form returned by _GetMIPSComponentStatuses. # Will by an empty hashref if the user does not have permission # to undo snapshots. # UNDOSNAPSHOTPERMISSION True if the user has permissions to undo snapshots ################################################################################ sub GetMIPSLegacyStatuses { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [ { ONEPLUS => [qw( FEDERALIDNUMBER PROVIDERID )], }, 'P4PPROGRAMID', 'USERNAME', ], [], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $provider_id = $args->{PROVIDERID}; my $username = $args->{USERNAME}; my $undo_snapshot_permission = AthenaSecurity::ResourceSecure($dbh, { RESOURCE => 'CLINICALSP4PCONTENT', USERNAME => $username, }); if ($undo_snapshot_permission) { my $legacy_statuses = Athena::P4P::App::PerformanceData::HandleMIPSLegacyData($dbh, { FEDERALIDNUMBER => $federal_id_number, P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, }); return { LEGACYCOMPONENTS => $legacy_statuses, UNDOSNAPSHOTPERMISSION => 1, }; } else { return { LEGACYCOMPONENTS => {}, UNDOSNAPSHOTPERMISSION => 0, }; } } ################################################################################ # FindDefaultYear # # Description: # Gets the mips program ids and finds the active programs # # Returns: # YEAR => The min year of active program ################################################################################ sub FindDefaultYear { my ($dbh) = @_; my @p4p_program_ids = Athena::P4P::App::PerformanceData::GetMIPSProgramList($dbh, { ENTITYTYPES => ['INDIVIDUAL'], }); my @active_programs = @{ Clinical::P4P::Submission::GetActiveSubmissionPrograms($dbh, { P4PPROGRAMIDS => @p4p_program_ids, IGNOREREPORTINGPERIOD => 0, }) || [] }; my @p4p_active_program_years = map { $_->{YEAR} } @active_programs; return min( @p4p_active_program_years); } ################################################################################ # _GetUniqueMeasurePerformanceColumns # # Description: # Returns the appropriate columns required from P4PMEASUREPERFORMANCE # # Returns: # A list of column names ################################################################################ sub _GetUniqueMeasurePerformanceColumns { my ($dbh, $args) = @_; my $is_mips_phr_enabled = $args->{MIPS_PHR_TOGGLE}; if ( $is_mips_phr_enabled ) { return UNIQUE_MEASURE_PERFORMANCE_COLUMNS(); } else { return [@{ UNIQUE_MEASURE_PERFORMANCE_COLUMNS() }, 'P4PMEASUREID']; } } ################################################################################ # GetMIPSACIToggleStatus # # Description: # Returns the appropriate status of rollout toggle for ACI category # # Returns: # TRUE/FALSE ################################################################################ sub GetMIPSACIToggleStatus { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMID )], [qw( ISMVP )], ); my $p4pprogramid = $args->{P4PPROGRAMID}; my $is_mvp = $args->{ISMVP} || 0; my $category = $is_mvp ? GetMVPCategory($dbh, $p4pprogramid) : GetMIPSCategory($dbh, $p4pprogramid); my $is_mips_phr_enabled = $category eq 'PI'; return $is_mips_phr_enabled; } ################################################################################ # HandleMIPSLegacyData # # Description: # Gets the legacy status for each MIPS component for a provider or TIN, # as well as whether each component is in a status where it can be unsnapshotted. # # Parameters: # Required: # FEDERALIDNUMBER The TIN whose MIPS statuses should be returned # and/or # PROVIDERID The provider whose MIPS statuses should be returned # # P4PPROGRAMID The id of one of the MIPS programs in the year # for which we are fetching data # # Returns: # Hashref where each key is a MIPS component (Quality, IA, ACI) ################################################################################ sub HandleMIPSLegacyData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [ { ONEPLUS => [qw( FEDERALIDNUMBER PROVIDERID )], }, 'P4PPROGRAMID', ], [], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $program_id_by_category = Athena::P4P::App::PerformanceData::GetMIPSProgramsByCategory($dbh, { P4PPROGRAMID => $p4p_program_id, }); my %return_structure; foreach my $category (sort keys %$program_id_by_category) { $program_id_by_category->{$category} = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $program_id_by_category->{$category}, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => $category, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', }); my $program_submission = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $program_id_by_category->{$category}, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); $return_structure{$category} = Clinical::P4P::Submission::Utils::MIPSSnapshotAssessment::GetLegacyReportingDetails($dbh, { P4PPROGRAMID => $program_id_by_category->{$category}, SUBMISSIONNUMBER => $program_submission->CurrentSubmissionNumber() || 1, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id }); $return_structure{$category}->{P4PPROGRAMID} = $program_id_by_category->{$category}; $return_structure{$category}->{SUBMISSIONNUMBER} = $program_submission->CurrentSubmissionNumber() || 1; if (!$return_structure{$category}->{LEGACYDATAREQUIREDYN}) { $return_structure{$category}->{LEGACYDATAREQUIREDYN} = 'N'; } } return \%return_structure; } ################################################################################ # _GetMIPSQMassQualityProgramIds # # Description: # Gets the program id from the above defined constant for MIPS Quality with QMaas # and returns based on given entity_type # # Parameters: # Required: # ENTITYTYPE The type of the program , GROUP or INDIVIDUAL # # CATEGORY The category of the MIPS Program # # Returns: # The Program of the MIPS Quality (Group or INDIVIDUAL) ################################################################################ sub _GetMIPSQMassQualityProgramIds { my ($args) = @_; my $entity_type = $args->{ENTITYTYPE}; my $category = $args->{CATEGORY}; my $category_data = _MIPS_QUALITY_FOR_QMASS_PROGRAM_DATA()->{$category}; return if ! defined $category_data; my $entity_type_data = $category_data->{ENTITYTYPE}; return if ! defined $entity_type_data; my $entity_type_instance = $entity_type_data->{$entity_type}; return if ! defined $entity_type_instance; my $full_year_data = $entity_type_instance->{FULLYEAR}; return if ! defined $full_year_data; return $full_year_data->{P4PPROGRAMID}; } ################################################################################ # _GetMIPSMessageForQRDA1Import # # Description: # Identify if we have to show the message on MIPS dashboard of full data, The message will not be shown if # either of : isqrdaimportdone, isstartlaterthanfced and issnapshotbufferover is true else message # will be shown on MIPS Dashboard to let the user know that Athena doesnt have their full data # # Parameters: # Required: # FEDERALIDNUMBER # YEARS # Optional: # PROVIDERID # # Returns: # The boolean value 0 or 1 ################################################################################ sub _GetMIPSMessageForQRDA1Import { my ( $dbh, $args ) = @_; Athena::Util::Assert::AssertFields( $args, [ qw( FEDERALIDNUMBER YEARS ) ], [ qw( PROVIDERID ) ], ); my $provider_id = $args->{PROVIDERID}; my $year = $args->{YEARS}->[0]; my $federal_id_number = $args->{FEDERALIDNUMBER}; my $entity_type = ($provider_id ne undef)?'INDIVIDUAL':'GROUP'; my $p4p_program_id = GetMIPSProgram($dbh, { CATEGORY => 'Quality', YEAR => $year, ENTITYTYPE => $entity_type, FEDERALIDNUMBER => $args->{FEDERALIDNUMBER}, PROVIDERID => $provider_id, } ); $p4p_program_id = @$p4p_program_id[0]; my $is_registered_in_no_ecqm = 1; if($p4p_program_id) { my $submissionenrollmentdata = Clinical::P4P::Submission::GetSubmissionEnrollmentData($dbh, { FEDERALIDNUMBERS => [ $federal_id_number ], P4PPROGRAMIDS => [ $p4p_program_id ], PROVIDERIDS => $provider_id ? [ $provider_id ] : undef, CHECKFIRSTENCOUNTERDATE => 1, }); if($submissionenrollmentdata->{ENROLLMENTDATA}->{$p4p_program_id}) { my %measure_subscription_ids = %{ $submissionenrollmentdata->{ENROLLMENTDATA}->{$p4p_program_id}->{$federal_id_number}->{$provider_id || -1} }; my $programdefinitionbyp4pprogramid = BusCall::P4P::GetProgramDefinition($dbh, { P4PPROGRAMID => $p4p_program_id, }); foreach my $measure_details(@{ $programdefinitionbyp4pprogramid->{Measures} }){ my $program_criterias = $measure_details->{Criteria}; for my $criteria(@$program_criterias) { if ($criteria->{ReportingType} eq "EHR" && $measure_subscription_ids{$criteria->{P4PMeasureSubscriptionID}} == 1) { $is_registered_in_no_ecqm = 0; last; } } last if($is_registered_in_no_ecqm == 0); } } } my ($first_encounter_date_data) = @{Clinical::P4P::Provider::GetP4PProviderData($dbh, { INCLUDEDUPLICATEPROVIDERS => 1, KEY => 'FIRSTENCOUNTERDATE', MIN => 1, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, })}; my $reportingperiodstartdate; my $reportingperiodend; my $snapshotbuffer; my $beginsnapshotting; my $programs = Clinical::P4P::Utils::GetProgramInfo($dbh); my $programinfo; if ( ($programs->{$p4p_program_id}) && $first_encounter_date_data && $first_encounter_date_data->{VALUE}) { $programinfo = $programs->{$p4p_program_id}; $reportingperiodstartdate = $programinfo->{REPORTINGPERIODSTART}; $reportingperiodend = $programinfo->{REPORTINGPERIODEND}; $beginsnapshotting = $programinfo->{BEGINSNAPSHOTTINGYN}; if ( $reportingperiodstartdate && $reportingperiodend && $beginsnapshotting eq 'Y') { my $isqrdaimportdone = Clinical::P4P::ReportingPeriod::IsQrdaImported($dbh, { P4PPROGRAMID => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number }); my $currentdate = AthenaDate::AthenaToday(); my $isstartlaterthanfced = AthenaDate::Date1IsLaterThanOrEqualToDate2( $reportingperiodstartdate,$first_encounter_date_data->{VALUE} ); return ( $isstartlaterthanfced || $isqrdaimportdone || $is_registered_in_no_ecqm ) ? 0 : 1; } } } ########################################################################################## # _GetMIPSIAMeasures # # Description: # Add MIPS IA related submission data to be stored in p4pmeasureperformance # # Parameters: # Required: # None # Optional: # NPIS ListRef[String] NPI numbers to filter to. TINONLY is a special # value, and will filter down to TIN enrollments. # P4PPROGRAMID ListRef[Integer] # FEDERALIDNUMBERS ListRef[String] # PROVIDERID ListRef[Integer] # # Return Value: # ListRef[String] ########################################################################################## sub _GetMIPSIAMeasures{ my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( )], [qw( NPIS FEDERALIDNUMBERS PROVIDERID P4PPROGRAMID ISMVP )], ); my $p4pprogramid = $args->{P4PPROGRAMID}; my $data = $args->{DATA}; my $provider_id = $args->{PROVIDERID}; my $federal_id_number = $args->{FEDERALIDNUMBERS}; my @keys = qw( DENOMINATOR EXCLUDED NUMERATOR PERFORMANCERATE ROUNDEDPERFORMANCERATE FINALMEASUREPOINTS WEIGHTEDMEASUREPOINTS DISPLAYPERFORMANCERATE ISINVERSEMEASURE STATUS MAXPOINTS ); my $reporting_period = Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => $p4pprogramid, }); my $program_year = $reporting_period->{PROGRAMYEAR}; my $is_mvp = $args->{ISMVP} || 0; my $mipscategory = $is_mvp ? GetMVPCategory($dbh, $p4pprogramid) : GetMIPSCategory($dbh, $p4pprogramid); my $entitytype = $is_mvp ? GetMVPEntityType($dbh, $p4pprogramid) : GetMIPSEntityType($dbh, $p4pprogramid); my $get_measure_data_index = "index(pdm P4PDENORMENROLL_PRGPRFESUBME)"; if (defined Athena::Conf::AthenaNet::InternalServices('qupro')->{get_measure_data_index_ia_measure}) { $get_measure_data_index = Athena::Conf::AthenaNet::InternalServices('qupro')->{get_measure_data_index_ia_measure}; } my $sql = Clinical::P4P::Persistence::MultiProviderSubmission->GetMeasureDataSQL({ INCLUDEFEDERALIDNUMBERS => 1, INCLUDEPROGRAMIDS => 1, INCLUDEPROVIDERIDS => 1, P4PPROGRAMID => $p4pprogramid, PROGRAMCATEGORY => $mipscategory, KEYS => \@keys, ENTITYTYPE => $entitytype, }); if ( $provider_id ) { $sql->Where( ["p4psubmissiongroup.providerid in (??)", $provider_id ], ); } elsif ( $federal_id_number ) { $sql->Where( ["p4psubmissiongroup.federalidnumber in (??)", $federal_id_number ], ); } my @allmeasuredata; if($program_year > 2022){ # 1.All taskcompletion measures now have a dummy p4pmeasure id and they are also enrolled , hence we can now filter by enrollment # 2. Some measures like PDMP , MDD have one measure and one taskcompletion measure in such cases enrolment in on the measure and to determine enrolment # we are using the ARGUMENTS.P4PMEASUREID in p4ppdcriteriadata my $performancedatasql = SQL::Select->new( )->Distinct( )->Select( "performancedata.*", )->From( ["(??) performancedata", $sql], "p4pdenormalizedenrollment pdm", "p4ppdcriteria", "p4ppdcriteriadata", )->Where( ["pdm.p4pprogramid in (??)", $p4pprogramid ], "performancedata.p4pprogramid = pdm.p4pprogramid", SQL->Or( "performancedata.providerid is null", "performancedata.providerid = pdm.providerid", ), SQL->Or( "pdm.federalidnumber is null", "performancedata.federalidnumber = pdm.federalidnumber", ), "p4ppdcriteria.id = p4ppdcriteriadata.p4ppdcriteriaid", "performancedata.p4ppdcriteriaid = p4ppdcriteria.id", SQL->Or( "performancedata.p4pmeasuresubscriptionid = pdm.p4pmeasuresubscriptionid", "performancedata.p4pmeasuresubscriptionid is null AND p4ppdcriteriadata.key = 'ARGUMENTS.P4PMEASUREID' AND pdm.p4pmeasureid = p4ppdcriteriadata.value ", ), "nvl(pdm.hiddenyn, 'N') = 'N'", "pdm.deleted is null", "pdm.guidelines = '0'", ); if($get_measure_data_index ne 'OFF') { $performancedatasql->Hints( $get_measure_data_index, ); } @allmeasuredata = $performancedatasql->TableHash($dbh); } else{ @allmeasuredata = $sql->TableHash($dbh); } @allmeasuredata = grep {$_->{P4PMEASURESUBSCRIPTIONID} ne undef || $_->{CRITERIACODE} =~ /^TASKCOMPLETION/ } @allmeasuredata; my $mips_ia_data = \@allmeasuredata; return $mips_ia_data; } ########################################################################################## # _AddReferenceMeasureNameForIA # # Description: # Adds reference measure name with IA measure and remove the HIE: Clinical Information Reconciliation and # Documentation of current medications measures from input list # # Parameters: # Required: # DATA : Measure hash # Optional: # None # # Return Value: # ListRef[String] ########################################################################################## sub _AddReferenceMeasureNameForIA{ my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( DATA )], [qw( )], ); my @results = @{$args->{DATA}}; my @iaresults = grep { $_->{CATEGORY} eq 'IA' } @results; my $programdefinition = BusCall::P4P::GetProgramDefinition($dbh, { P4PPROGRAMID => $iaresults[0]->{P4PPROGRAMID}, }); my $measures = $programdefinition->Measures(); my @criterias = map {@{ $_->Criteria() || [] }} @{$measures}; foreach my $record(@iaresults) { my @criteriacode = grep { $_->CriteriaCode() eq $record->{CRITERIACODE} } @criterias; my @method = map { $_->Method() } @criteriacode; if(scalar @method > 0 and $method[0] ne undef){ my @p4p_measure_ids = map { $_->P4PMeasureID() } @method; my @referencemeasurename = map { $_->MeasureName() } grep { $_->P4PMeasureID() eq $p4p_measure_ids[0]; } @criterias; $record->{REFERENCEMEASURENAME} = $referencemeasurename[0]; } } return \@results; } ################################################################################ # GetEnrolledPrograms # # Description: # Gets where the provider is enrolled in both PI program # # Parameters: # Required: # FEDERALIDNUMBER The TIN whose MIPS statuses should be returned # and/or # PROVIDERID The provider whose MIPS statuses should be returned # # P4PPROGRAMIDS The ids of the MIPS PI programs in the year # for which we are fetching data # # Returns: # boolean value 1 if enrolled in both PI program else 0 ################################################################################ sub GetEnrolledPrograms { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMIDS ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }], [qw( ALLDUPLICATES )], ); my @p4p_program_ids = @{$args->{P4PPROGRAMIDS}}; my $providerid = $args->{PROVIDERID}; my $federalidnumber = $args->{FEDERALIDNUMBER}; my $includeallduplicates = $args->{ALLDUPLICATES}; my $npitinprovidermap; my @duplicateproviderids; push (@duplicateproviderids, $providerid); if($includeallduplicates && $providerid) { $npitinprovidermap = Clinical::P4P::Provider::GetNPITINProviderMap($dbh, { ALLDUPLICATES => 1, }); @duplicateproviderids = @{$npitinprovidermap->{ $providerid }{ $federalidnumber } }; } my $enrolledproviderssql = SQL::Select->new( )->Distinct( )->Select( 'vp4penrollment.p4pprogramid', )->From( 'vp4penrollment', 'provider', 'medicalgroup', )->Joins( 'vp4penrollment.providerid = provider.id', 'provider.medicalgroupid = medicalgroup.id (+)', )->Where( ['vp4penrollment.p4pprogramid in (??)', \@p4p_program_ids ], 'provider.username is not null', 'provider.deleted is null', ); # Restriction by TIN pre-empts restriction by provider. if ( $federalidnumber ) { $enrolledproviderssql->Where( ['medicalgroup.federalidnumber = ?', $federalidnumber], ); } if ( $providerid && !$includeallduplicates ) { $enrolledproviderssql->Where( ['provider.id = ? ', $providerid] ); } elsif ($providerid && $includeallduplicates) { $enrolledproviderssql->Where( ['provider.id IN (??) ', \@duplicateproviderids] ); } my @enrolledproviders = $enrolledproviderssql->TableHash($dbh); return \@enrolledproviders; } ################################################################################ # GetProgramIDForSubmission # # Description: # Returns the program id from the list of programids # # Parameters: # Required: # P4PPROGRAMIDS # ENTITYTYPE # MIPSCOMPONENT # # # Returns: # program id for the given year , category and entitytype ################################################################################ sub GetProgramIDForSubmission { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMIDS ENTITYTYPE MIPSCOMPONENT ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }], [qw( YEAR ISMVP )], ); my $p4p_program_id = $args->{P4PPROGRAMIDS}; my $federal_id_number = $args->{FEDERALIDNUMBER}; my $entity_type = $args->{ENTITYTYPE}; my $provider_id = $args->{PROVIDERID}; my $mips_component = $args->{MIPSCOMPONENT}; my $is_mvp = $args->{ISMVP} || 0; my $year = $args->{YEAR} ? $args->{YEAR} : Clinical::P4P::ReportingPeriod::Get($dbh, { P4PPROGRAMID => @$p4p_program_id[0], })->ProgramYear(); if ($mips_component eq 'PI' || $is_mvp ) { my $enrolled_program_list = Athena::P4P::App::PerformanceData::GetEnrolledPrograms($dbh, { P4PPROGRAMIDS => $p4p_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, ALLDUPLICATES => 1, }); my $pi_data = Athena::P4P::App::PerformanceData::_GetEntityTypeData({YEAR=>$year, CATEGORY=> $mips_component, ENTITYTYPE => $entity_type, ISMVP => $is_mvp}); $p4p_program_id = $pi_data->{$entity_type}->{FULLYEAR}->{P4PPROGRAMID}; my $is_mips_pi_90_days_enabled = Athena::RolloutToggle::GetEnabledVersion($dbh, { KEY => 'MIPS_PI_QTLY', }) eq 'ON'; if ($is_mips_pi_90_days_enabled) { my @ninetydayslist = split(m/[,]/, $pi_data->{$entity_type}->{NINETYDAYS}->{P4PPROGRAMID}); my @fullyearlist; if($is_mvp) { @fullyearlist = split(m/[,]/, $p4p_program_id); push (@ninetydayslist, @fullyearlist); } foreach my $ninetyday (@ninetydayslist) { if (grep {$_->{P4PPROGRAMID} eq $ninetyday } @$enrolled_program_list) { $p4p_program_id = $ninetyday; last; } } if($is_mvp && scalar @$enrolled_program_list == 0) { $p4p_program_id = $fullyearlist[0]; } } else { if (grep {$_->{P4PPROGRAMID} eq $pi_data->{$entity_type}->{NINETYDAYS}->{P4PPROGRAMID} } @$enrolled_program_list) { $p4p_program_id = $pi_data->{$entity_type}->{NINETYDAYS}->{P4PPROGRAMID}; } } } else { $p4p_program_id = @$p4p_program_id[0]; } return $p4p_program_id; } ################################################################################ # GetRepresentativeProviderNameBasedClinicalEncouterY # # Description: # Returns the listref of hashrefs of those providers whose minm providerids has # ClinicalEncouterCheckYN is set to Y for the given NPI # Parameters: # Required: # DATAS listref of hashrefs of MIPS PerformanceData # # # # Returns: # listref of hashrefs of MIPS PerformanceData with updated provider name ################################################################################ sub GetRepresentativeProviderNameBasedClinicalEncouterY { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields($args, [qw( DATAS )], [qw( VALIDATEENROLLMENTSTATUS YEAR )], ); my $datas = $args->{DATAS}; my $year = $args->{YEAR}; my $validateenrollmentstatus = $args->{VALIDATEENROLLMENTSTATUS}; my %groupbydata = Athena::Util::Hash::HashGroupBy(['NPI'], @{$datas}); my @npis = keys %groupbydata; my %providerinfo = %{Clinical::P4P::Utils::GetRepresentativeProviderInfo($dbh,{ NPIS => \@npis, YEAR => $year, })}; my @updatedlist; foreach my $data (@$datas) { if (exists($providerinfo{$data->{NPI}}{$data->{FEDERALIDNUMBER}}[0]->{PROVIDERID})) { if ( $providerinfo{$data->{NPI}}{$data->{FEDERALIDNUMBER}}[0]->{PROVIDERID} ne $data->{PROVIDERID} ) { $data->{PROVIDERNAME} = $providerinfo{$data->{NPI}}{$data->{FEDERALIDNUMBER}}[0]->{PROVIDERNAME}; } push (@updatedlist, $data); } elsif (($validateenrollmentstatus ? ($data->{PROVIDERNAME} eq 'All' && $data->{NPI} eq undef && $data->{ENROLLMENTSTATUS} eq '1') : ($data->{PROVIDERNAME} eq 'All' && $data->{NPI} eq undef))) { push (@updatedlist, $data); } } return \@updatedlist; } ################################################################################ # _GetEligibilityStatus # # Description: # Evaluates whether the given entity is eligible for submission based on # the eligibility status of the entity. # # Parameters: # Required: # # YEAR Scalar Year to check the eligibilty for. # At least one of: # FEDERALIDNUMBER Scalar TIN for which to retreive data # PROVIDERID Scalar ID of the provider for whom to retreive data # # # Returns: # String. A string that represents the current status of eligibility. ################################################################################ sub _GetEligibilityStatus { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( YEAR ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }], [qw( )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $performance_year = $args->{YEAR}; # get eligibility status for this entity my $eligibilitystatussql = Athena::P4P::Persistence::ProgramEligibility::GetMIPSEligibilityStatus($dbh,{ FEDERALIDNUMBER => $federal_id_number, YEAR => $performance_year, PROVIDERID => $provider_id, }); my $iseligible; my @results = $eligibilitystatussql->TableHash($dbh); my $mipseligibilitystatus = $results[0]->{MIPSELIGIBILITYSTATUS}; return $mipseligibilitystatus; } ################################################################################ # _UpdateMetadataForEligibilityStatus # # Update the eligibility status. # # Parameters: # Required: # # YEAR Scalar Year to check the eligibilty for. # MIPSCOMPONENT String Component to retreive eligibilty. # At least one of: # FEDERALIDNUMBER Scalar TIN for which to retreive data # PROVIDERID Scalar ID of the provider for whom to retreive data # SUBMISSIONBLOCKINGREASON String Submission blocking reason # # Returns: # String submission blocking reason. ################################################################################ sub _UpdateMetadataForEligibilityStatus { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( YEAR MIPSCOMPONENT ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }], [qw( SUBMISSIONBLOCKINGREASON )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $year = $args->{YEAR}; my $mips_component = $args->{MIPSCOMPONENT}; my $submission_blocking_reason = $args->{SUBMISSIONBLOCKINGREASON} || ''; my $username = $Global::session{USERNAME} || 'ATHENA'; my $p4p_program_id = Athena::P4P::App::PerformanceData::GetMIPSProgram($dbh, { CATEGORY => $mips_component, YEAR => $year, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); $p4p_program_id = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $p4p_program_id, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => $mips_component, YEAR => $year, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', }); # MIPS is actually represented by 3 separate programs - # quality, ACI and IA. We want to replicate the composite score # across all three program submission object, and store the category scores # in the appropriate submission objects. my $mips_program_ids_by_category = Athena::P4P::App::PerformanceData::GetMIPSProgramsByCategory($dbh, { P4PPROGRAMID => $p4p_program_id, }); my $category_program_id; foreach my $category ( keys %{ $mips_program_ids_by_category } ) { $category_program_id = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $mips_program_ids_by_category->{$category}, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => $category, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', }); my $category_submission_object = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $category_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); # If the submission object does not exist for some reason, move on. next if $category_submission_object->NoDenormalizedData(); my $lower_case_category = lc $category; $category_submission_object->UpdateMetaData({ USERNAME => $username, DATA => { SUBMISSION_BLOCKING_REASON => $submission_blocking_reason, }, }); } return $submission_blocking_reason; } ################################################################################ # GetSubmissionStatus # # Update the eligibility status. # # Parameters: # Required: # # P4PPROGRAMID Integer P4P Program associated with the category. # At least one of: # FEDERALIDNUMBER Scalar TIN for which to retreive data # PROVIDERID Scalar ID of the provider for whom to retreive data # # Returns: # String submission blocking reason. ################################################################################ sub GetSubmissionStatus { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMID ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }], [qw( )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $mips_program_ids_by_category = Athena::P4P::App::PerformanceData::GetMIPSProgramsByCategory($dbh, { P4PPROGRAMID => $p4p_program_id, }); my $submission_blocking_reason = ''; my $category_program_id; foreach my $category ( keys %{ $mips_program_ids_by_category } ) { $category_program_id = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $mips_program_ids_by_category->{$category}, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => $category, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', }); my $category_submission_object = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $category_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); # If the submission object does not exist for some reason, move on. next if $category_submission_object->NoDenormalizedData(); my $lower_case_category = lc $category; my $metadata = $category_submission_object->GetMetaData ({ KEYS => [qw( SUBMISSION_BLOCKING_REASON )], }); $submission_blocking_reason = $metadata->{SUBMISSION_BLOCKING_REASON}; if($submission_blocking_reason ne '') { last; } } return $submission_blocking_reason; } ################################################################################ # IsAllMIPSCategerorySubmitted # # Update the eligibility status. # # Parameters: # Required: # # P4PPROGRAMID Integer P4P Program associated with the category. # At least one of: # FEDERALIDNUMBER Scalar TIN for which to retreive data # PROVIDERID Scalar ID of the provider for whom to retreive data # # Returns: # Boolean if all categories are submitted or not. ################################################################################ sub IsAllMIPSCategerorySubmitted { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( P4PPROGRAMID ), { ONEPLUS => [qw( PROVIDERID FEDERALIDNUMBER )], }], [qw( )], ); my $federal_id_number = $args->{FEDERALIDNUMBER}; my $provider_id = $args->{PROVIDERID}; my $p4p_program_id = $args->{P4PPROGRAMID}; my $mips_program_ids_by_category = Athena::P4P::App::PerformanceData::GetMIPSProgramsByCategory($dbh, { P4PPROGRAMID => $p4p_program_id, }); my $isallmipscategorysubmitted = 1; my $category_program_id; foreach my $category ( keys %{ $mips_program_ids_by_category } ) { $category_program_id = Athena::P4P::App::PerformanceData::GetProgramIDForSubmission($dbh, { P4PPROGRAMIDS => $mips_program_ids_by_category->{$category}, FEDERALIDNUMBER => $federal_id_number, PROVIDERID => $provider_id, MIPSCOMPONENT => $category, ENTITYTYPE => $provider_id ? 'INDIVIDUAL' : 'GROUP', }); my $category_submission_object = Clinical::P4P::Persistence::ProgramSubmission->new({ DBH => $dbh, P4PPROGRAMID => $category_program_id, PROVIDERID => $provider_id, FEDERALIDNUMBER => $federal_id_number, }); # If the submission object does not exist for some reason, move on. next if $category_submission_object->NoDenormalizedData(); my $submission_number = $category_submission_object->CurrentSubmissionNumber() || 1; my $status_hash = $category_submission_object->GetStatusBySubmissionNumber({ SUBMISSIONNUMBER => $submission_number, }) || {}; my $program_status = $status_hash->{STATUS}; if($program_status ne 'SUBMITTED') { $isallmipscategorysubmitted = 0; #break if we get any category submission status as not submitted last; } } return $isallmipscategorysubmitted; } ################################################################################ # _MergeNewWithExistingData # # Call merge operation for merging new measure performance with existing data. # # Required Parameters: # DATA - Arrayref - List of hashrefs containing rows to insert into P4PMEASUREPERFORMANCE. # Should be return value of FetchMIPSMeasurePerformanceData. # # P4PPROGRAMID - Integer - Used to remove denormalized data for this program ID # that was not otherwise being updated per $args->{DATA}. # USERNAME - String - Username to make DB commits with. # # Returns: # void. ################################################################################ sub _MergeNewWithExistingData { my ($dbh, $args) = @_; Athena::Util::Assert::AssertFields( $args, [qw( DATA P4PPROGRAMID USERNAME )], [qw( ISMIPSPHRENABLED SUBGROUPID )], ); my $data = $args->{DATA}; my $username = $args->{USERNAME}; my $p4pprogramid = $args->{P4PPROGRAMID}; my $context_id = Athena::Util::Database::SessionInfo($dbh)->{context}; my $is_mips_phr_enabled = $args->{ISMIPSPHRENABLED} || undef; my $subgroupid = $args->{SUBGROUPID}; while(@$data) { my $newdata = [splice @$data, 0, 100]; if($subgroupid ne undef){ foreach my $datum (@$newdata) { $datum->{SUBGROUPID} = $subgroupid; } } my $begin = time(); Athena::Util::SQL::ProcessTable($dbh, { OPERATION => 'Merge', TABLENAME => 'P4PMEASUREPERFORMANCE', TABLEROWS => $newdata, USERNAME => $username, COLUMNNAMES => P4PMEASUREPERFORMANCE_COLUMNS(), UNIQUECOLUMNNAMES => _GetUniqueMeasurePerformanceColumns($dbh, { MIPS_PHR_TOGGLE => $is_mips_phr_enabled }), }); my $timetakenformerge = time() - $begin; my $count = scalar(@$newdata); Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( '{%.3f} Time taken for merge - complete - complete', $timetakenformerge, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); Clinical::P4P::Log::Log($dbh, { LEVEL => 'performance', MESSAGE => sprintf( 'No of records for merge::'.$count, ), SCRIBEIDENTIFIER => 'p4psubmissionlogs', TAGS => { CONTEXTID => $context_id, P4PPROGRAMID => $p4pprogramid, }, }); } } 1;
Leave a Comment