Bar Chart - Updating data property

Hi,

I’ve built a template for a bar chart component that displays historical data for either the day, month, or year. My issue is that for some reason the Bar Chart component doesn’t update it’s .data property when the the parameters used in its property expression changes.

For clarity, I’m using a runScript() expression to fetch historical information to display in the chart, based on the selection made in a Tab Strip component. Changes made to the Tab Strip doesn’t result in the Bar Chart running the script again.

In my search for a solution I’ve defined three separate custom properties, each one (pre)-fetching one of the possible datasets I may need to display, and then using a Case() property expression in the Bar Chart to select between them. The data fetches correctly, and the Bar Chart shows whichever one is the default, but once again, changes to the Tab Strip doesn’t update the data based on the simple property expression statement.

I’m currently using three (!) separate Bar Charts, each one fetching data for one of the three possible time periods, and then binding the .visible property to the selection of the Tab Strip. Strangely, the Bar Chart honors the logic in the .visible property, while ignoring the same logic in its .data property.

Any ideas where I’m going wrong with this?

Kind regards,
Jacques

1 Like

Show your various expressions.

Hi @pturmel

OK, to simplify, I've broken it up into a few stages. First, I define three custom properties (datasets): Day, Month and Year. These have the following Property Expression:

runScript("project.tagHistory.tagBarChart", 0, {KPI Bars.data::Meta.TagPath} ), 'day' )

This function works and the properties take on the correct datasets.

Then, I define a 4th custom property called tagdata, with the following Property Expression:

case( {KPI Bars.Tab Strip.selectedTab},
'year', {KPI Bars.Year},
'month', {KPI Bars.Month},
{KPI Bars.Day}
)

This function ensures a valid selection, and assigns one of the three other Custom Properties to this one, depending on the selection in the Tab Strip.

Again, this appears to work correctly, and the data updates as I make selections on the Tab Strip.

Finally, I assign the Data property to this custom property via a straight Property mapping.

KPI Bars.tagdata

This is where the fun begins. The Bar Chart will accept the initial Dataset assigned when it first runs, but will not respond to changes in the Tab Strip.

To test where things are going wrong, I assign both the Custom Property (tagdata) and this Data Property for the Bar Chart to some labels on the template. The labels show me the size of the dataset mapped to it by default, which is sufficient for my testing needs. All three datasets of the original mapped Properties are different in size.

These should match each other at all times, since one is mapped to the other. But as I change the selection of the Tab Strip, I can see the Custom Property changing, but the Data Property on the Bar Chart remains unchanged. The data on the Bar Chart also remains at the initial selection, without updating.

As mentioned in my original post, if I make three Bar Charts, each linked to one of the datasets, and I do the following with their Visible properties:

{KPI Bars.Tab Strip.selectedTab} = 'day'

The Visible Property updates correctly and everything works as expected. So clearly the Bar Chart refreshes it's properties, but apparently not for changes in its Data property.

Interesting. Sounds like a bug. Just for kicks, instead of a direct property binding from .tagdata to .data, try with an expression binding with just the .tagdata reference. Might narrow it down.

Consider deleting the bindings on the three time period properties, leaving the data there. Then export and post the window for the rest of us to play with. (Or give to support, if the content is too sensitive.)

Hi @pturmel

Great tips, thanks! So, following your suggestions, I’ve broken the bindings on the 3 Custom Properties, and the Bar Chart updates correctly, now.

Your second suggestion, to use an expression binding didn’t have any effect different from a direct property binding.

So, now we know that binding the data property to another property that is updated with a tagScript() function doesn’t work. Is this a bug, or is there a workaround or protection that I can write into the expression?

I’ve tried using a value other than 0 for the tagScript() pollrate function, and it slowed my interface down so much that I had to force close it and start a new session.

Kind regards,
J

Well, runScript is like any other expression function – it only executes when established and then when an operand changes. Unless there’s a timer setting. Your tagpath reference never changes, and the string ‘day’ never changes, so you shouldn’t expect those datasets to be updated but once when the window opens. The case expression should still pick from among them, though. Please share the code in the project.tagHistory.tagBarChart function. Or share it all with support.

Oh, and yes, you should not do anything slow in a runScript expression – it runs on the event dispatch thread. Try to use a tag history binding to get tag history, and parameterize its conditions. That keeps the actual query in the background. If you must, use invokeAsynchronous to script your tag history queries, but not from within runScript. (You can’t return a background result from runScript.)

Hi @pturmel

The function is really only a queryTagHistory() call. Here it is:

def tagBarChart( tag="", window="day" ):
"""
tagBarChart retrieves a dataset fit for consumption in a bar chart

window values are: "day", "month", and "year"
"""

TimeZone = +2;

headers = [ "Label", tag ]

if system.tag.exists( tag ):
# Datermine the start and end dates for the calculation
today = system.date.now()

  if window == "year":
  	# Select most recent complete week as endDate (Sunday @ midnight)
  	endDate = system.date.addDays(
  		system.date.parse( system.date.format(today, "yyyy-MM-dd"), "yyyy-MM-dd" ),
  		( int( system.date.format(today, "u") ) - 1 ) * -1 )
  	endDate = system.date.addHours( endDate, TimeZone )
  	intervalHours = 7 * 24 
  	rangeHours =  intervalHours * 30
  	lblFormat = "w"
  elif window == "month":
  	# Select most recent complete day as endDate
  	endDate = system.date.parse( system.date.format(today, "yyyy-MM-dd"), "yyyy-MM-dd" ) 
  	endDate = system.date.addHours( endDate, TimeZone )
  	intervalHours = 24
  	rangeHours = intervalHours * 30  
  	lblFormat = "dd"
  else:
  	# Select most recent complete hour as endDate
  	endDate = system.date.parse( system.date.format(today, "yyyy-MM-dd hh"), "yyyy-MM-dd hh" ) 
  	intervalHours = 1
  	rangeHours = intervalHours * 24 
  	lblFormat = "kk"
  
  import re
  pattern = re.compile('^\[[A-Za-z0-9]+\]/')
  if not pattern.match( tag ):
  	tag = '[default]/' + tag
  # Run the query
  try:
  	valueSet = system.tag.queryTagHistory(
  		paths = [ tag ], 
  		endDate = endDate,
  		rangeHours = rangeHours,
  		intervalHours = intervalHours,
  		aggregationMode = "Average" )
  except:
  	data = []
  	valueSet = system.dataset.toDataSet( headers, data )
  data = []
  for row in range( valueSet.getRowCount() ):
  	rowData = [ system.date.format(valueSet.getValueAt( row, 0 ), lblFormat ) ]
  	for col in range( 1, valueSet.getColumnCount() ):
  		rowData.append( valueSet.getValueAt( row, col ) )
  	data.append( rowData )
  valueSet = system.dataset.toDataSet( headers, data )

else:
data =
valueSet = system.dataset.toDataSet( headers, data )

return valueSet

Disclaimer: I'm a hacker, nor a developer!

Any feedback welcome.

Kind regards,
J

I would encourage you to break that operation up into separate bindings:

  1. custom properties with the date endpoint expression bindings you need,
  2. A tag history query binding to retrieve the data for the tag of interest using (1), and
  3. A runscript binding that accepts the data from (2) as an argument and returns the dataset massaged as desired for the bar chart.

The latter could be wrapped around the case() expression to do this in one step.

Hi @pturmel,

Thanks for your comments. I’ve implemented them, but the problem persists. I’ve pursued this avenue as far as I can, eventually doing away with runScript() entirely, in favor of a script tied to the Tab Strip’s propertyChange() event.

Once again, I can see the script updating custom properties, but it cannot change the Bar Chart’s data property.

I can report that I can use your history query binding directly, and that does work. So it seems that the Bar Chart balks at any attempt to pragmatically alter it’s data property.

This is unfortunate, but I’ve had to settle for the three Bar Chart solution for now. Any further advice will be appreciated and I’ll try any new ideas until we find a solution.

Kind regards,
J

I don't think there's anything anyone can offer until this is replicated by others. Please post a simplified version of this project that shows this behavior, or ask support to look over your shoulder to see what else might be going wrong. If there is a collection of conditions that trigger a bug, support would have to get involved to get a fix scheduled.

For posterity, I’ve managed to solve the issue satisfactorily by defining expression tags that call the runScript() function (one for each time horizon), and then making references from the template to the tags.

This is as opposed to trying to make those same runScript() calls from custom properties on the Bar Chart element itself.

Relegating the function calls to the tag engine, and separating them from the internal function of the Bar Charts, gets around the issue of data updates I’ve been experiencing.