lsgs / redcap-extended-reports

Provides various additional options to enhance REDCap's built-in reporting functionality.
GNU General Public License v3.0
1 stars 4 forks source link

array_key_exists exception thrown when 0 records in the dataset have configured repeating fields #32

Open dr01d3r opened 3 days ago

dr01d3r commented 3 days ago

Given a project that has repeating instruments, and a simple report that outputs both repeating and non-repeating data, if none of the $rows rendered by doReshapedReport have instances of the repeating instrument, the module will throw an exception.

The 'extended_reports' module threw the following exception when calling the hook method 'redcap_every_page_before_render':

TypeError: array_key_exists(): Argument #1 ($key) must be a valid array offset type in C:\inetpub\wwwroot\modules\extended_reports_v2.2.0\Report.php:1486
Stack trace:
#0 C:\inetpub\wwwroot\modules\extended_reports_v2.2.0\Report.php(1486): array_key_exists(Array, Array)
#1 C:\inetpub\wwwroot\modules\extended_reports_v2.2.0\Report.php(1468): MCRI\ExtendedReports\Report->makeChoiceDisplayHtml('survey_status', Array, Array)
#2 C:\inetpub\wwwroot\modules\extended_reports_v2.2.0\Report.php(1254): MCRI\ExtendedReports\Report->makeChoiceDisplay(Array, 'survey_status', 'html', '.', ',')
#3 C:\inetpub\wwwroot\modules\extended_reports_v2.2.0\Report.php(593): MCRI\ExtendedReports\Report->makeOutputValue(Array, 'survey_status', 'html', '.')
#4 C:\inetpub\wwwroot\redcap_v14.6.7\Classes\Cache\CacheManager.php(207): MCRI\ExtendedReports\Report->doExtendedReport('html')
#5 C:\inetpub\wwwroot\redcap_v14.6.7\Classes\Cache\CacheManager.php(187): Vanderbilt\REDCap\Classes\Cache\CacheManager->retrieveAndCacheData('715ceaac353dd09...', Array, Array, Array)
#6 C:\inetpub\wwwroot\redcap_v14.6.7\Classes\Cache\States\ReadThroughState.php(32): Vanderbilt\REDCap\Classes\Cache\CacheManager->handleCacheMiss('715ceaac353dd09...', Array, Array, Array)
#7 C:\inetpub\wwwroot\redcap_v14.6.7\Classes\Cache\CacheManager.php(132): Vanderbilt\REDCap\Classes\Cache\States\ReadThroughState->getOrSet(Array, Array, Array)
#8 C:\inetpub\wwwroot\modules\extended_reports_v2.2.0\Report.php(206): Vanderbilt\REDCap\Classes\Cache\CacheManager->getOrSet(Array, Array, Array)
#9 C:\inetpub\wwwroot\modules\extended_reports_v2.2.0\ExtendedReports.php(58): MCRI\ExtendedReports\Report->viewReport()
#10 C:\inetpub\wwwroot\redcap_v14.6.7\ExternalModules\classes\ExternalModules.php(3167): MCRI\ExtendedReports\ExtendedReports->redcap_every_page_before_render('773')
#11 C:\inetpub\wwwroot\redcap_v14.6.7\ExternalModules\classes\ExternalModules.php(3334): ExternalModules\ExternalModules::startHook('extended_report...', 'v2.2.0', Array)
#12 C:\inetpub\wwwroot\redcap_v14.6.7\ExternalModules\classes\ExternalModules.php(3367): ExternalModules\ExternalModules::ExternalModules\{closure}('extended_report...', 'v2.2.0')
#13 C:\inetpub\wwwroot\redcap_v14.6.7\Classes\Hooks.php(41): ExternalModules\ExternalModules::callHook('every_page_befo...', Array)
#14 C:\inetpub\wwwroot\redcap_v14.6.7\Classes\System.php(1029): Hooks::call('redcap_every_pa...', Array)
#15 C:\inetpub\wwwroot\redcap_v14.6.7\Config\init_project.php(7): System::initProjectPage()
#16 C:\inetpub\wwwroot\redcap_v14.6.7\DataExport\report_ajax.php(9): require_once('C:\\inetpub\\wwwr...')
#17 {main}

I've narrowed it down to the following code:

https://github.com/lsgs/redcap-extended-reports/blob/89a1bd7069c014eb1dc08ff6781c934cff5a3d5c/Report.php#L963-L964

Here's some example $rows data after doReshapedReport. The first 2 fields are flat, and the 2nd two exist on the same repeating instrument.

    [0] => Array
        (
            [0] => Array
                (
                    [0] => 306
                    [1] => 2024-09-25
                    [2] => Array
                        (
                        )

                    [3] => Array
                        (
                        )

                )

        )

And the header info for one of those repeating fields configured in the report:

            [3] => Array
                (
                    [report_id] => 2362
                    [project_id] => 773
                    [arm_id] => 787
                    [arm_name] => Arm 1
                    [event_id] => 2321
                    [descrip] => Week 1
                    [in_filter] => 1
                    [form_name] => paccer_weekly_survey
                    [field_name] => paccer_weekly_survey_complete
                    [field_order] => 4
                    [element_type] => select
                    [unique_name] => week_1_arm_1
                    [element_label] => PACC-ER Weekly Survey Complete?
                    [subvalues] => Array
                        (
                        )

                    [is_repeating_event] => 
                    [is_repeating_form] => 1
                    [instance_count] => 0
                    [view_rights] => 3
                    [export_rights] => 1
                )

So the noted lines of code default the value to an empty array, but since the field is part of the headers and the instance_count is 0, the field stays an empty array.

This eventually causes the following line to fail due to an array being used as the key:

https://github.com/lsgs/redcap-extended-reports/blob/89a1bd7069c014eb1dc08ff6781c934cff5a3d5c/Report.php#L1483

When there is at least one record with data in those repeating fields, the data looks more like this:

            [4] => Array
                (
                    [0] => 306
                    [1] => 2024-09-25
                    [2] => Array
                        (
                            [0] => 
                            [1] => 
                        )

                    [3] => Array
                        (
                            [0] => 
                            [1] => 
                        )

                )

And

            [3] => Array
                (
                    [report_id] => 2362
                    [project_id] => 773
                    [arm_id] => 787
                    [arm_name] => Arm 1
                    [event_id] => 2321
                    [descrip] => Week 1
                    [in_filter] => 1
                    [form_name] => paccer_weekly_survey
                    [field_name] => paccer_weekly_survey_complete
                    [field_order] => 4
                    [element_type] => select
                    [unique_name] => week_1_arm_1
                    [element_label] => PACC-ER Weekly Survey Complete?
                    [subvalues] => Array
                        (
                        )

                    [is_repeating_event] => 
                    [is_repeating_form] => 1
                    [instance_count] => 2
                    [view_rights] => 3
                    [export_rights] => 1
                )
dr01d3r commented 3 days ago

@lsgs Given all this info, I wish I had any insight to a solution, but not really sure how you'd want to approach this. The problem is global absence of data, and it can be tricky when trying to render headers and rows that are supposed to repeat. Perhaps if you were able to determine that the entirety of an instrument is absent data across all rows, you simply omit those columns?