Automating Redash Dashboards: Sending Charts to Mattermost
A Python script solves the task of automatically delivering charts from Redash to Mattermost. It fetches data via API, recreates visualizations in matplotlib, combines them into a single dashboard, and sends it to a channel. Ideal for teams where data updates with a delay and manual dashboard review is inefficient.
The pipeline runs on a cron job: it checks data freshness, builds charts with annotations, and publishes the final report before the workday begins.
Fetching Data from Redash API
Redash provides an API to execute queries by query_id with parameters. The main function:
import requests
BASE_URL = 'https://your-redash-instance'
HEADERS = {'Authorization': 'Key YOUR_API_KEY'}
def fetch_query(query_id, params):
response = requests.post(
f'{BASE_URL}/api/queries/{query_id}/results',
headers=HEADERS,
json={'parameters': params, 'max_age': 0},
)
return response.json()
If the result isn't ready, the API returns a job — implement polling to wait for completion. For dashboards, parse parameters from the URL:
from urllib.parse import parse_qs, urlparse
def parse_dashboard_url(url):
parsed = urlparse(url)
params = parse_qs(parsed.query)
return {
key: value[0]
for key, value in params.items()
}
Convert dynamic dates like 'last 7 days' manually:
from datetime import datetime, timedelta
def last_7_days():
today = datetime.utcnow()
return {
'start': (today - timedelta(days=7)).strftime('%Y-%m-%d'),
'end': today.strftime('%Y-%m-%d')
}
Building and Annotating Charts
Visualizations are built in matplotlib, mimicking Redash styles: colors, line types, sizes. A basic line chart example:
import matplotlib.pyplot as plt
def build_chart(df):
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(df['date'], df['value'], linewidth=2)
ax.plot(df['date'], df['plan'], linewidth=2)
ax.set_title('Metric dynamics')
return fig
The key step is annotations for readability in static images. Add the latest value, week-over-week change, deviation from plan:
ax.annotate(
f"value: {last_value}",
xy=(last_date, last_value),
xytext=(10, 10),
textcoords='offset points'
)
This allows metrics to be understood without interactivity.
Combining Charts into a Dashboard
Multiple charts (by metrics, categories) are merged into one image using PIL for better UX:
from PIL import Image
def merge_images(images, output_path):
base = Image.new('RGB', (1200, 800), 'white')
for img, pos in images:
base.paste(img, pos)
base.save(output_path)
Add section headers by department or theme for navigation.
Checks and Duplicate Protection
The script checks for the target date in the data:
if df['date'].max() != target_date:
raise Exception('Data not updated yet')
Protection against duplicate sends via a state file:
from pathlib import Path
STATE_FILE = Path('last_sent.txt')
def was_sent(date):
return STATE_FILE.exists() and STATE_FILE.read_text() == str(date)
Sending to Mattermost
Use mattermostdriver to upload the file and post:
from mattermostdriver import Driver
mm = Driver({
'url': 'your-mattermost',
'token': 'BOT_TOKEN',
})
mm.login()
mm.posts.create_post({
'channel_id': channel_id,
'message': 'Report ready',
'file_ids': [file_id],
})
Key Points
- Automation reduces manual effort: charts arrive in the messenger automatically upon data updates.
- Annotations on charts ensure full informativeness without hover interactions.
- Combining into one dashboard improves UX in chat.
- Freshness and duplicate checks guarantee pipeline reliability.
- Easy to scale: add new query_ids and parameters.
— Editorial Team
No comments yet.