KoolReport's Forum

Official Support Area, Q&As, Discussions, Suggestions and Bug reports.
Forum's Guidelines

Strange CustomDrilldown issue #559

Open Alex Chartier opened this topic on on Dec 13, 2018 - 37 comments

Alex Chartier commented on Dec 13, 2018

I have a custom drill down that has 5 levels. When I traverse the drilldrown from the top level it works fine. If I however start the drilldown from the second level I get very strange results. Let me explain by what I mean starting at the second level.

The 5 levels are Summary, Monthly, Weekly, Daily and OneDay. I have these set in the mainReport in the DrillDown settings function and again in the view subReports.

If I simply remove the Summary report from both lists the drilldown starts properly on the Monthly report but as soon as I click on a column it appears as if the weekly report is called recursively. I attach a picture of what the breadcrumbs tree looks like:

When I start at the Summary report it looks fine, see this picture:

Any ideas on what might be happening? I can provide full code lists if you like. They are long as I am populating and filtering a table to build the datasource but I can cut and past relevant parts if you want.

Thanks.

KoolReport commented on Dec 14, 2018

It could be that the Monthly report needs parameters from Summary, however, when you start directly with Monthly, the parameters is missing and cause the unexpected results. Could you please check.

Alex Chartier commented on Dec 14, 2018

The Summary report passes the following to the Monthly report:

			"clientEvents" => array(
				"itemSelect"=>"function(params){ 
					$drilldown.next({event:params.selectedRow[0]})

The Monthly report tests whether the event is in the params and if not uses all events passed from the index.php file in the scope. When the Monthly report runs it passes the following to the Weekly report:

		"clientEvents" => array(
			"itemSelect"=>"function(params){ 
			$drilldown.next({month:params.selectedRow[0]".$this->drillDownEvent."});

The $this->drillDownEvent is the event passed from Summary through Monthly if it was passed. It is in the format ', event:eventname'. If it was not passed then drillDownEvent is a null string. So now the Weekly Report also determines whether an event param exists and if not then again uses the event list passed in the scope.

The code seems to work correctly. If you note the multiple Weekly items in the breadcrumbs, if I click on any of them the correct report displays as expected.

Any other info I can provide?

Andrew Borell commented on Dec 14, 2018

I wish I could help on this one -- im starting to work on a drilldown so maybe I will run into a similar issue and can extend some advice. Not sure how the breadcrumbs are tracked but it seems like a recursion problem that you would encounter with an invalid starting index in an array. Everything should be 0-based indexing in everything you ever do with arrays. If your array start index is 1 you will have problems. The back-end is probably adding 1 from 0 and for each iteration then returning "weekly" as your next item for the length of the array until the last item which in that case it probably checks end($array) and sees its the last item.

Alex Chartier commented on Dec 14, 2018

Perhaps, but if that were the case it should probably recurse indefinitely and eventually run out of memory. It seems to always recurse the same number of times at each level. I will go back through the code and make sure my source data is always zero based.

Alex Chartier commented on Dec 14, 2018

Nope, I zero base all the datasource arrays before using them with array_values($this->dataSource) and I use $this->dataSource as my dataSource in the ColumnChart::create call.

Was a good idea though.

Alex Chartier commented on Dec 14, 2018

I also build an array of subReports in the mainReport file for the return in the settings function. I then do array_keys($subReports) for the subReports entry in the CustomDrillDown::create call so I know the subReports are zero indexed and in the order I added them to the $subReports array.

the funny thing is the subReports are being called in the correct order, and if I click on the first of the repeating reports I can drill down to the next one in sequence although that next one in sequence will also recurse. I can repeat this process down to the final report which does not recurse.

Andrew Borell commented on Dec 14, 2018

I will review the drilldown documentation. Are you using the classic or the other? nvm. You said Custom Drilldown.

Alex Chartier commented on Dec 14, 2018

Custom Drilldown. All my data sources are arrays of data that is coming from an event system. Each report is a column chart except the last which is a simple table of transactions on a single day. Had great fun figuring out the grouping option for the table. Works very well once you understand it.

Andrew Borell commented on Dec 14, 2018

If I understand you correctly, you are bypassing the main report and hitting the subreport directly. How are you creating the customDrillDown object if you never hit the main report? This is where your array of subreports is generated.

e.g. from examples )

    <?php
        \koolreport\drilldown\CustomDrillDown::create(array(
            "name"=>"saleLocation",
            "title"=>"Sale By Location",
            "subReports"=>array("countrySale","citySale"),
        ));
    ?> 

ETA: I missed your update above cuz I didnt refresh. Let me review again.

ETA2: The question still remains. You do build the customDrillDown in the main report. The way I read your earlier post it sounded like you hit the subreport first though. That object wouldn't exist if you did not hit the main report first. Therefore it sounds to me like the drilldown is passing itself to itself because the current index is all it is aware of.

Alex Chartier commented on Dec 14, 2018

Not exacly. I am bypassing the first subReport. I am passing a value into the main report file which indicates where I want to start. This builds the subReport array: `

	$this->subReports = array();
	switch ($this->showBy) {
		case DisplayType::Summary:
			$this->subReports["summaryReport"] 	= SummaryReport::class;
		case DisplayType::ByMonth:
			$this->subReports["monthlyReport"] 	= MonthlyReport::class;
		case DisplayType::ByWeek:
			$this->subReports["weeklyReport"] 	= WeeklyReport::class;
		case DisplayType::ByDay:
			$this->subReports["dailyReport"] 	= DailyReport::class;
		case DisplayType::OneDay:
			$this->subReports["oneDayReport"] 	= OneDayReport::class;
	}

	return ["subReports" => $this->subReports];
and in the view:
CustomDrillDown::create(array(
	"name"=>"eventDrillDown",
	"title"=>"",
	"subReports"=>array_keys($this->subReports),
	"showLevelTitle"=>true,
	"scope"=>["transactionsTable"=>$this->transactionsTable, "selectedEvents"=>$this->selectedEvents],
));

Andrew Borell commented on Dec 14, 2018

Excellent. This is enough info I can try to reproduce the issue later this evening. I was thinking you would need the landing page to act as a report view broker of sorts to pull this off and your code looks very close to what I had considered with that switch function.

KoolReport commented on Dec 15, 2018

I just to want to overview of how CustomDrillDown works. CustomDrillDown utilize the sub report feature of KoolReport to perform drilldown.

In a report, you may have many subreports. This is nothing related to drilldown. The most important feature of subreport is the ajax loading. By defining the lists of subreport inside the main report, we can load any subreport via ajax without reloading page. We list all the subreport inside the settings() of main report as you see above.

Now we have list of subreport in term of $key=>$value in which $key is the instance name of subreport and $value is the model name of subreport.

In the CustomDrillDown that we create in main report's view, we enter the sequences of sub reports used for drilldown in array. In the first render of CustomDrillDown, it will render the first sub report in the array. The scope will be sent to client as well. When user performs selection on the first level report, we call the next() function. This function will send all the parameters that you want to send to next levels. Important: The scope is attached as well.

So when CustomDrillDown at server receives the next() request from client, it will start forwarding all parameters + scope to the next sub reports in the list to render. Later the view will be send to client-side for rendering.

Hope my explanation is good.

Regards,

Karl Wilken

Alex Chartier commented on Dec 15, 2018

Thanks Karl. That is how I understand it.

Alex Chartier commented on Dec 15, 2018

Andrew, made an interesting observation. When I start with monthlyReport and then do a drilldown I get weeklyReport showing twice in the breadcrumbs, but, if I look at the actual html output the div that is being displayed is the dailyReport but with the weeklyReport content.

More digging to do.

Andrew Borell commented on Dec 15, 2018

Yah, what I was wondering when I went to test this ( havent had a moment ) is whether you are creating a smaller array of subreports depending on the landing page where you broker out the view. If you include the monthly as a subreport and its the landing page report I think it would have this problem, given the way arrays work.

i.e) your customDrillDown::Create needs to be specific based on the main report view, I think. So in addition to pointing to the class in the switch, you would want to do a customDrillDown::create in each to specify the subreports. This would likely have an affect on whether drilldown recognizes the landing page main report as a subreport, and the overall length of the array.

What you are doing is more dynamic than the drilldown was intended to be I think, but I dont see any reason why it shouldnt work as long as everything is initialized properly.

Alex Chartier commented on Dec 15, 2018

The landing report is always the same. If I start with Summary then it is always the landing report for all drilldowns. if I start with Monthly then it is always the landing point. I don't change the subReports list once the starting report is selected.

Andrew Borell commented on Dec 15, 2018

I get what Karl is saying.

Have you considered a quick alternative ( workaround ) such as triggering a client side event? I notice the example online uses "<a href="javascript:saleLocation.back(0)">All countries</a>" in the breadcrumb where saleLocation is the name given to the drilldown. Karl mentions the drilldown uses next() to traverse the drilldown. Perhaps you could use client-side events to load the main page, then immediately trigger javascript:YourCustomDrillDownNameHere.next(1); after evaluating the parameters to see which page is desired?

Alex Chartier commented on Dec 15, 2018

And to further clear things up I was concerned it may have something to do with running the report under Joomla but I extracted all the Joomla stuff and ran the report in native php with the same results.

Alex Chartier commented on Dec 15, 2018

Not sure I follow Andrew. Easy enough to add client events but which one? I assume Nexting? Are you suggesting that in the nexting event I trigger the drillDown.next call? Not following. Can you explain further please?

Andrew Borell commented on Dec 15, 2018

Probably by just adding a script to your view that if a condition is met to include it, otherwise do not.

e.g.) in your view:

echo "<script type=\"text/javascript\">
		KoolReport.load.onDone(function() {
			myDrilldownName.next(1);
		} );
</script>";	

Alternatively maybe you can load your classes dynamically. I apologize in advance if this idea looks insane. I have no idea if this will even work. Just throwing some basic code out in theory.

// autoload up here
//include all your classes even if you dont intend to use them.  

// set default landing page. 
$lp = 'summaryReport';

// this is our list of potential reports 
$def = array("monthlyReport","weeklyReport","dailyReport","dayOfReport");

// assume skip = 1 in teh GET variable to get Monthly as the 
// landing page. But first we initialize it.  And the $lp=def[0] below
// should actually be the first page before the subreports start, whatever it may be. 
// but I am too lazy to write that logic in. 
$skip = 0;
isset($_GET['skip']) ? ($skip = $_GET['skip'])  && ($lp = $def[0]) :  $skip = 0 ;

// our monthlyReport would drop out 
// of the array of subreports if skip = 1 
$subs = array_slice($def,$skip ,count($def)-$skip); 

//create an instance of the main class 	
$r = new MyDrilldown($subs);
// render the specific view you want to start with
$r-run()-render($lp . '.view.php');

class MyDrilldown extends \koolreport\KoolReport
{
    use \koolreport\core\SubReport;
	
	public $subs; 

	// dont know if this __contruct is gonna be a problem ... 
	public function __construct($subs){
		
		$this->subs = $subs;
		$this->subReports = array();
		
		foreach($subs as $k=>$v){
			// add to array, and uppercase first char for class name. 
			$this->subReports[{$v}] = ucfirst($v) . '::class';	
			} 
		}
	
    function settings(){
		
        return array(
            "subReports"=>$this->subReports
			);
		}
	}	

Alex Chartier commented on Dec 16, 2018

I am not sure how that is any different than what I am already doing. When I start on Monthly the Summry report is not in the subReport list, jsut what you are doing.

By the way, if you code a construct function you have to call the parent at the end or it won't work.

Andrew Borell commented on Dec 16, 2018

Sorry, hard for me to tell if you were building that array on the fly at the main page and executing the the method to render a specific view. If I come up with any other ideas on what may be causing this issue I will post back.

Alex Chartier commented on Dec 16, 2018

Would it be useful for me to send you the full source files?

Alex Chartier commented on Dec 17, 2018

I have noticed something that I think is significant. The number of times it recurses is equal to the number of different xAxis entities I have in the datasource. The reason it doesn't fail when I start at the summary is that any drilldown from there has only one xAxis entity.

I will continue to investigate.

Alex Chartier commented on Dec 17, 2018

So I have no idea why the drilldown is doing what it is doing. I know it has something to do with the xAxis but I can't see what in my data or setup would cause this. Like many problems the longer you stare at it the less visible certain things become so I am hoping fresh eyes will see something that I can't. Below is the datasource arrays and ColumnChart setups. In a previous post I included my CustomDrilldown details.

Hopefully someone will be able to see the issue and let me know.

Monthly datasource array
array (0 => array('month' => 'August', 'Oktoberfest 2018' => 275, 'Pancake Breakfast' => 0, 'Italian Dinner' => 0, 'Christmas Cocktail' => 0, 'New Year\'s Eve Boog-a-loo' => 0,),
  1 => array ('month' => 'September', 'Oktoberfest 2018' => 2625, 'Pancake Breakfast' => 0, 'Italian Dinner' => 0, 'Christmas Cocktail' => 0, 'New Year\'s Eve Boog-a-loo' => 0,),
  2 => array ('month' => 'October', 'Oktoberfest 2018' => 0, 'Pancake Breakfast' => 255, 'Italian Dinner' => 710, 'Christmas Cocktail' => 0, 'New Year\'s Eve Boog-a-loo' => 0,),
  3 => array ('month' => 'November', 'Oktoberfest 2018' => 0, 'Pancake Breakfast' => 10, 'Italian Dinner' => 190, 'Christmas Cocktail' => 3025, 'New Year\'s Eve Boog-a-loo' => 0,),
  4 => array ('month' => 'December', 'Oktoberfest 2018' => 0, 'Pancake Breakfast' => 0, 'Italian Dinner' => 0, 'Christmas Cocktail' => 400, 'New Year\'s Eve Boog-a-loo' => 820,),
)
ColumnChart::create(array(
	"dataSource"=>$this->dataSource,
	"title"=>"Monthly Transaction Report",
	"columns"=>array ( 'month',
		  'Oktoberfest 2018' => array ('label' => 'Oktoberfest 2018', 'type' => 'number', 'prefix' => '$',),
		  'Pancake Breakfast' => array ('label' => 'Pancake Breakfast', 'type' => 'number', 'prefix' => '$',),
		  'Italian Dinner' => array ('label' => 'Italian Dinner', 'type' => 'number', 'prefix' => '$',),
		  'Christmas Cocktail' => array ('label' => 'Christmas Cocktail', 'type' => 'number', 'prefix' => '$',),
		  'New Year\'s Eve Boog-a-loo' => array ('label' => 'New Year\'s Eve Boog-a-loo', 'type' => 'number', 'prefix' => '$',),
		),
	"clientEvents" => array(
		"itemSelect"=>"function(params){ 
		$drilldown.next({month:params.selectedRow[0]".$this->drillDownEvent."});
		}",
	),
));

Weekly datasource array
array (0 => array ('week' => 'Oct 29', 'Italian Dinner' => 150, 'Pancake Breakfast' => 0, 'Christmas Cocktail' => 0,),
  1 => array ('week' => 'Nov 05', 'Italian Dinner' => 40, 'Pancake Breakfast' => 10, 'Christmas Cocktail' => 0,),
  2 => array ('week' => 'Nov 12', 'Italian Dinner' => 0, 'Pancake Breakfast' => 0, 'Christmas Cocktail' => 100,),
  3 => array ('week' => 'Nov 19', 'Italian Dinner' => 0, 'Pancake Breakfast' => 0, 'Christmas Cocktail' => 2500,),
  4 => array ('week' => 'Nov 26', 'Italian Dinner' => 0, 'Pancake Breakfast' => 0, 'Christmas Cocktail' => 425,),
)
ColumnChart::create([
	"dataSource"=>$this->dataSource,
	"title"=>"Weekly Report",
	"columns"=>array ( 'week',
	  'Italian Dinner' => array ('label' => 'Italian Dinner', 'type' => 'number', 'prefix' => '$',),
	  'Pancake Breakfast' => array ('label' => 'Pancake Breakfast', 'type' => 'number', 'prefix' => '$',),
	  'Christmas Cocktail' => array ('label' => 'Christmas Cocktail', 'type' => 'number', 'prefix' => '$',),
	),
	"clientEvents" => [
		"itemSelect"=>"function(params){ 
			$drilldown.next({week:params.selectedRow[0]".$this->drillDownEvent."});
		}",
	],
	"options"=>[
		"barThickness"=>100,
		"scales"=> [
			"yAxes"=>[["ticks"=>["suggestedMin"=>0]]
			]
		]
	]
]);
Andrew Borell commented on Dec 17, 2018

What is a yAxes? Is that a legit property or should it be yAxis?

Alex Chartier commented on Dec 17, 2018

Typo, shouldn’t affect the problem.

Alex Chartier commented on Dec 18, 2018

I have made a very simple test report including the data as above that exhibits the problem. In order to run change the require_once in the index.php to the location of the koolreport library. You will note that once you click on a column in the monthly report the weekly report recurses 5 times.

I have zipped up the report file and sent it to your support email.

KoolReport commented on Dec 18, 2018

I have received your email. I will look into it.

Alex Chartier commented on Dec 20, 2018

Make any progress? Are you able to replicate the issue?

KoolReport commented on Dec 20, 2018

Please give our dev.team a little more time. I will ask them tomorrow.

Alex Chartier commented on Jan 1, 2019

Any news? Has dev.team been able to replicate the problem?

KoolReport commented on Jan 2, 2019

Yes, we saw the issue. It happens when chart has multiple series. Each series will fire "itemSelect". That's why you got 5 times because there are 5 series on your charts. Here is the add-on code:

		"clientEvents" => array(
			"itemSelect"=>"function(params){
				if(params.selectedColumnIndex==1)
				{
					$drilldown.next({month:params.selectedRow[0]});
				}
			}",
		),

Above code will ensure there is only one request sent to server.

KoolReport commented on Jan 2, 2019

Further on this, our dev.team will find way to fix so that itemSelect will return exactly the clicked bar chart. Above is just temporary solution.

KoolReport commented on Jan 3, 2019

Just to let you know that the issue has been solved and the working of event "itemSelect" is the same like in Google Charts. The fix will be available in the new version of KoolReport. Pro.

Alex Chartier commented on Jan 3, 2019

Initial testing with the myReport files I sent you seems to work. I need to do some further testing with my production system. First try and the second level drill down did not find any data. Will investigate further and advise.

Alex Chartier commented on Jan 3, 2019

My new problem is related to the change in year, so unrelated to the reported issue. The problem appears to be corrected with the code provided. It would be good however to have the baseline code corrected so as not to require the if statement in the function.

Thanks.

Build Your Excellent Data Report

Let KoolReport help you to make great reports. It's free & open-source released under MIT license.

Download KoolReport View demo
help needed

DrillDown