Gramex is a platform that allows users to create visual storyboards from data.
This guide creates a simple dashboard for SuperStore, a fictional supermarket. We’ll analyse the supermarket’s sales by product segment, region and category.
In order to start this tutorial, we will need to:
store-sales.csv
.This is what SuperStore’s sales data looks like:
Our goal is to display the sales by product segment, region and category. The relevant fields are:
After finishing this tutorial, you will be able to:
Our application should look like:
superstore
anywhere.gramex.yaml
in it. Leave it blank for now.index.html
in it. Just enter “Hello Gramex” in it.Open a terminal, change to the superstore
folder and start Gramex:
conda activate gramex
gramex
You’ll see log messages on the terminal. Once you see a line Listening on port 9988
, Gramex is ready.
...
INFO 22-Apr 13:34:26 __init__ PORT Listening on port 9988
INFO 22-Apr 13:34:26 __init__ 9988 <Ctrl-B> opens the browser...
Note: Gramex may print other lines after this – so it may not be the last line on the log.
Open your browser and visit http://localhost:9988
.
You’ll see “Hello Gramex”.
Let’s create a URL that sends data to the dashboard. We’ll use a Gramex microservice called
FormHandler
.
Add this to the (currently empty) gramex.yaml
:
url:
superstore-data:
pattern: /$YAMLURL/data
handler: FormHandler
kwargs:
url: $YAMLPATH/store-sales.csv
Now visit http://localhost:9988/data
in your browser.
You should now see a JSON payload representing the first 1,000 lines of the dataset:
[
{ "Order ID":"CA-2016-152156","Order Date":"08-11-2016",... },
{ "Order ID":"CA-2016-152156","Order Date":"08-11-2016",... },
// etc
]
You could also visit http://localhost:9988/data?_limit=10&_format=html to see the first ten rows as a simple HTML table.
Since we now have access to the data from a REST API, we are ready to start building the frontend.
First, add this code into gramex.yaml
:
import:
ui:
path: $GRAMEXAPPS/ui/gramex.yaml # Import the UI components
YAMLURL: $YAMLURL/ui/ # ... at this URL
This imports the UI components library, which includes:
We won’t change gramex.yaml
for the rest of this tutorial. We are done with the backend configuration.
Now, copy this code into index.html
. It’s a boilerplate that includes the CSS and JS files we’ll need.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>SuperStore Sales Dashboard</title>
<link rel="stylesheet" href="ui/bootstraptheme.css" />
</head>
<body>
<div class="placeholder">This div shall hold our data</div>
</body>
<script src="ui/jquery/dist/jquery.min.js"></script>
<script src="ui/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="ui/lodash/lodash.min.js"></script>
<script src="ui/g1/dist/g1.min.js"></script>
<script src="ui/vega/build/vega.min.js"></script>
<script src="ui/vega-lite/build/vega-lite.min.js"></script>
</html>
The simplest and sometimes most effective way to represent data can be a table. Accordingly, Gramex provides a way of embedding tabular data in any HTML page as an interactive table.
To show the data as a table, insert the following lines in index.html:
<div class="formhandler" data-src="data"></div>
<script>
$(".formhandler").formhandler({ pageSize: 5 });
</script>
After saving the file, when we open
http://localhost:9988
,
we should see a table similar to the one below.
The table is interactive. Try playing around with it. Here’s a few things you could try:
Let’s add a simple barchart to display data grouped by Segment. Formhandler automatically does the grouping for us simply by changing the URL. Adding a ?_by
query to any FormHandler URL, like data?_by=Segment, changes the output: each of our numeric columns now has the sum of all rows having a particular Segment value.
FormHandler lets us do a lot of data querying, filtering and grouping just by editing the URL. See FormHandler Filters for list of all possible values.
To actually draw the chart, we’ll use a library called
Vega-lite. Vega-lite is a really simple to
use, configuration driven javascript charting library and supports many common
chart types. To draw a chart, we add a few pieces to our index.html
.
Add the following chart specification to your HTML, followed by some JS code to render the chart.
<div id="chart"></div>
<script>
var spec = {
$schema: "https://vega.github.io/schema/vega-lite/v3.json",
description: "A bar chart that sorts the y-values by the x-values.",
width: 360,
height: 200,
data: { url: "data?_by=Segment" },
mark: "bar",
encoding: {
y: {
field: "Segment",
type: "nominal",
sort: { encoding: "x" },
axis: { title: "Segment" },
},
x: {
field: "Sales|sum",
type: "quantitative",
axis: { title: "Sales" },
},
},
};
var view = new vega.View(vega.parse(vegaLite.compile(spec).spec))
.renderer("svg")
.initialize("#chart")
.hover()
.run();
</script>
After saving the file, when we open
http://localhost:9988
, we should see a chart and table like below.
Details of the specification can be found in the vega-lite docs, but some things to note:
width
, height
, data
, etc{"url": "data?_by=Segment"}
Sales|sum
and Segment
respectively,
telling Vega-lite to plot those quantities from the data that FormHandler
returns.We can now flex front-end muscle to make our dashboard look slightly better. We will keep this section short, but frontend appearances can be endlessly configured. Feel free to go through the rest of our guides to get a better handle on some of these.
Let’s add a second chart to plot the aggregate sum of Quantity by Segment. It’s the same chart - we are just changing the axes. Thus, we can reuse the earlier specification, but we still need to change values of certain fields. So we create a function to which we can pass the fields that need to be updated: the div to draw the chart, the x-axis column name and the title of the chart.
function render_charts(chartid, xfield, title) {
spec.title.text = title;
spec.encoding.x.field = xfield;
var view = new vega.View(vega.parse(vegaLite.compile(spec).spec))
.renderer("svg")
.initialize(chartid)
.hover()
.run();
}
render_charts("#chart1", "Sales|sum", "Sales by Segment");
render_charts("#chart2", "Quantity|sum", "Quantity by Segment");
Here are a few more ways in which we can tweak our dashboard:
If you have followed along with this quickstart, you now have a basic idea of how to build a simple static dashboard with Gramex. To see more of what Gramex’s functionality and features, including how to build interactive, URL driven dashboards; look at:
gramex --listen.port=<port number>
to run on an arbritary port.gramex setup ui
and restart gramex. If it still doesn’t work, open an issue on github or email cto@gramener.com