Graphing data using <sparkline>

The <sparkline> tag is something we can use to draw simple graphs on Tiles. It’s syntax is as such:

<sparkline [type] [width]>{comma,separated,values}</sparkline>

… where …

[type] is one of: bar, line, or area
[width] specifies the width of the graph

You can create a graph on a Tile that looks like this:

Using sometihng as simple as the following code for the Tile Template:

<block><panel #FF000000><sparkline line 300>1,2,8,3,9,7,4,3,9,13,7</sparkline></panel></block>

Of course that list of numbers is not very useful, so instead we need to gather some interesting data, and present it to the sparkline in a comma-separated format.


This tutorial will show how to create a sparkline graph on 2 different tiles using data from Ticket Sales.

Both of the graphs are using summary data from the 1st of the month, up to today.

  • Tickets Graph shows the Ticket Counts for each day.
  • Accounts Graph shows the Average Sale amount per Ticket for each day.

Tickets Tile Template

<block 0,0,20,20 #00FFFFFF right 800><size 70><bold>Tickets</bold></size><br/>
<size 40>
Total Tickets: [=TN('{REPORT TICKET COUNT}')]<br/>
Total Orders: [=TN('{REPORT ORDER COUNT: }')]
[=(TN('{REPORT TICKET COUNT:(TS.Status=Unpaid)}') > 0 ? '<br/><italic>Open Tickets:</italic> ' + TN('{REPORT TICKET COUNT:(TS.Status=Unpaid)}') : '')]
[=(TN('{ACCOUNT TOTAL:Receivables}') > 0 ? '<br/><italic>Unpaid:</italic> ' + F(TN('{ACCOUNT TOTAL:Receivables}'),'0.00') : '')]
</size>
</block><br/>
<block><sparkline bar 880>{CALL:db.getRowDelim('@@SalesPerDayChart','counts')}</sparkline></block>

Accounts Tile Template

<block 0,0,20,20 #00FFFFFF right 800><size 90><sym></sym> <bold>Accounts</bold></size><br/>
<size 70>
Sales: [=F(-1*(TN('{ACCOUNT TOTAL:Sales}')+TN('{ACCOUNT TOTAL:Sales Tax T1}')+TN('{ACCOUNT TOTAL:Sales Tax T2}')),'0.00')] ([=F(-1*(TN('{ACCOUNT TOTAL:Sales}')+TN('{ACCOUNT TOTAL:Sales Tax T1}')+TN('{ACCOUNT TOTAL:Sales Tax T2}'))/20,'0.00')])
</size>
</block><br/>
<block><sparkline area 880>{CALL:db.getRowDelim('@@SalesPerDayChart','averages')}</sparkline></block>

The specific portion of each Template that creates the graphs is this:

<sparkline bar 880>{CALL:db.getRowDelim('@@SalesPerDayChart','counts')}</sparkline>
<sparkline area 880>{CALL:db.getRowDelim('@@SalesPerDayChart','averages')}</sparkline>

So each of the Tiles uses a {CALL:X} tag to call the same JScript handler (db) and function (getRowDelim()), and the same SQL script (@@SalesPerDayChart). The only difference between the 2 Tiles is the type of graph (bar or area), and the last parameter where we specify which aggregate data we want to retrieve from the SQL.


@@SalesPerDayChart

declare @StartDate datetime = convert(varchar(25),GETDATE(),126)
declare @EndDate datetime = convert(varchar(25),GETDATE(),126)

/************************************/

SET @StartDate = convert(varchar(8),@StartDate,126)+'01'
SET @EndDate = DATEADD(Month,1,@StartDate)

declare @TotalSales money = 0.00

declare @tbl_Sales table
(
[ID]  INT IDENTITY(1,1) NOT NULL 
,[Date] varchar(10)
,[Amount] decimal(9,2)
,[Tickets] int
,[AvgTx] decimal(5,2)
,[Percent] decimal(5,2)
)

INSERT INTO @tbl_Sales
SELECT
convert(varchar(10),[Date],126)
, SUM( [TotalAmount]) as [Amount]
, COUNT(1) as [Tickets]
,0,0
FROM [dbo].[Tickets]
WHERE [Date]>=@StartDate AND [Date]<@EndDate
GROUP BY convert(varchar(10),[Date],126)
ORDER BY convert(varchar(10),[Date],126)

INSERT INTO @tbl_Sales
SELECT 'TTL', sum([Amount]), sum([Tickets]), 0, 0 FROM @tbl_Sales

set @TotalSales = (SELECT [Amount] FROM @tbl_Sales WHERE [Date]='TTL')

UPDATE @tbl_Sales SET
 [AvgTx] = (CASE WHEN [Tickets]=0 THEN 0 ELSE [Amount]/[Tickets] END)
,[Percent] = [Amount]/@TotalSales * 100

SELECT * FROM @tbl_Sales

The SQL script produces aggregate data in the following format:

The last 4 columns are the interesting bits.

  • Amount (‘amounts’) - the Transaction Amount for the Tickets for the Day
  • Tickets (‘counts’) - the Count of Tickets for the Day
  • AvgTx (‘averages’) - the Average Transaction Amount per Ticket for the Day
  • Percent (‘percents’) - the Percent of Sales Amount for the Day divided by the Total Sales Amount for the entire Period

We can use JScript to execute the SQL query, and then parse the output to pull out whichever column we want and pivot the data so that it becomes a comma-separated list of values.

db.getRowDelim(sqlcmd,dtype)

function getRowDelim(sqlcmd,dtype) {
  dtype = typeof dtype !== 'undefined' ? dtype : 'amounts';
  var row;
  var ids='';
  var dates='';
  var amounts='';
  var counts='';
  var averages='';
  var percents='';
  
  var r = sql.Query(sqlcmd).Delimit('~').All;
  var recs = r.Length;
  
  for (var i=0; i<recs; i++) {
    row = r[i].split('~');
    if (i!=0 && row[1]!='TTL') {
      ids+=',';
      dates+=',';
      amounts+=',';
      counts+=',';
      averages+=',';
      percents+=',';
    }
    if (row[1]!='TTL') {
    ids+=row[0];
    dates+=row[1];
    amounts+=row[2];
    counts+=row[3];
    averages+=row[4];
    percents+=row[5];
    }
  }
  switch (dtype) {
    case 'ids':
      return ids;
    case 'dates':
      return dates;
    case 'amounts':
      return amounts;
    case 'counts':
      return counts;
    case 'averages':
      return averages;
    case 'percents':
      return percents;
    default:
      return amounts;
  }
}

Using the SQL, and the dtype parameter to select the data we want, we choose from the following types, and return pivoted, comma-separated values for each day of the month:

'ids'
'dates'
'amounts'
'counts'
'averages'
'percents'

So if we call the script with:

getRowDelim('@@SalesPerDayChart','counts')

… then the returned data looks like this:

Alternatively, if we call the script with:

getRowDelim('@@SalesPerDayChart','averages')

… then the returned data looks like this:

3 Likes

If you wanted to have a graph that gave you data for different periods, you would change these lines in the SQL:

The original Tutorial is configured to pull data from the 1st of the Month up until Today:

SET @StartDate = convert(varchar(8),@StartDate,126)+'01'
SET @EndDate = DATEADD(Month,1,@StartDate)

For the last Month (previous 30/31 days from today):

SET @EndDate = convert(varchar(10),GETDATE(),126)
SET @StartDate = DATEADD(Month,-1,@EndDate)

For the last week (previous 7 days from today):

SET @EndDate = convert(varchar(10),GETDATE(),126)
SET @StartDate = DATEADD(Week,-1,@EndDate)

For the last 5 days from today:

SET @EndDate = convert(varchar(10),GETDATE(),126)
SET @StartDate = DATEADD(Day,-5,@EndDate)

Since January 1st of this year (2015):

SET @EndDate = convert(varchar(10),GETDATE(),126)
SET @StartDate = '2015-01-01'

3 Likes

While the Navigation Tiles were the original place where <sparkline> was developed and used, it is not the only place that can use the Tag.

For example, this is a Custom Entity Screen containing 4 Label Widgets:

The code for each is very similar - they all work with the same SQL data, but the type of aggregate used in each is different

Amounts:

<block>Amounts<br/><sparkline bar 940>{CALL:db.getRowDelim('@@SalesPerDayChart','amounts')}</sparkline></block>

Counts:

<block><size 12>Counts</size><br/><sparkline bar 940>{CALL:db.getRowDelim('@@SalesPerDayChart','counts')}</sparkline></block><br/>
[='{CALL:db.getRowDelim('@@SalesPerDayChart','counts')}'.replace(',',' ')]

Averages:

<block><size 12>Averages</size><br/><sparkline line 940>{CALL:db.getRowDelim('@@SalesPerDayChart','averages')}</sparkline></block>

Percents:

<block><size 12>Percents</size><br/><sparkline area 940>{CALL:db.getRowDelim('@@SalesPerDayChart','percents')}</sparkline></block>
3 Likes

… reserved for updates …

This is great. I’ll be glad to hear ideas about improving that feature.

A Couple things that I came across so far.

  • color parameter for sparkline
  • height parameter for sparkline
  • need to be fairly specific in setting the width to ensure all data is displayed, maybe a scale option, or auto-scaling to width and/or height.
  • angle/pivot/rotation parameter or…

Placing the sparkline in a Label is ok, but the Angle property of the Label widget is ignored - not sure if this by design or an oversight. I am now trying some things with an entity button, since it obeys the Angle parameter. This took a lot of tweaking and trial/error to get this lined up, but ultimately, this would be of great use if it were easier to do…

Then if the dataset isn’t consistent, all H-E-doublehockeysticks breaks loose…

Oh well, I will put this one on the back-burner for a while now…

That’s approaching some fine looking stuff which certainly will turn some heads during a SambaPOS presentation! Nice work :sunglasses:

Can I ask a simple question @QMcKay - can you show me the point and syntax used to generate your dialogue boxes with the return data? Not part of the final process but will be used a lot during any development of spark lines - thanks.

@QMcKay what you are looking for is something that works inside grids. Like a progress bar.

Script Test function produces the dialog boxes containing the return …

2 Likes

I thought Grids might have that feature already. I will take a look.

1 Like

Grids have that but I have to enable this feature.

is this working ?? how to make it work

Yes is working, Qs screenshots are of it in use.
Your question is vaigue…
How do I use? That would depend on where you want to use it.
The tag and sample code shows how to use.
The sparkline tag rendered a line/bar chart using the list of values returned by the report/SQL query.

Yes I mean How to use it, where to put the code and the sql , any scrren shots step by step ? please

The JScript and SQL Scripts go here:

##Automation > Scripts


The code containing <sparkline> tags was originally intended to go in Tile Templates, which are in the Navigation section of Automation Commands. But you can also put the code inside Label Widgets, or Automation Command Button Widget Captions.

Done As you said ,
result error
what i am missing

ontest string
this message

Go to reports screen and load the report. Then try those.

not working
same error !

You cannot run the test facility from the SQL Script. It does not work from @@SalesPerDayChart.

For the test facility to work, you need to be in the area where the JScript is located… so run the test from the section containing the function named `getRowDelim()’.

1 Like