Every week, course teams ask the same questions: what got watched, where did viewers stop, what got rewatched. Most answer from memory or not at all. This article builds the report that answers them, from your own view data, on a schedule.
The whole thing is one Python script on a Monday cron. It reads your FastPix Video Data export, computes the week's numbers, renders HTML, and emails it.
Here's the report you'll have by the end, generated from the sample dataset below. The numbers, drop-off points, rewatch sections, and needs-attention list all come from the sample export.
How the data flows
FastPix Player SDK ──▶ playback beacon ──▶ FastPix Video Data ──▶ CSV export ──▶ this script ──▶ weekly report
(Monday cron) (email to content team)fastpix-video-data-sample-export.csv: https://drive.google.com/file/d/1iMRkZEryzGc0_7GdAc_eTzd4Xluz-ZCu/view?usp=sharing
80 real (anonymized) views from an online course catalog, exported from the FastPix Video Data API. 136 columns per view, including the full playback event stream. Every snippet below runs against it. No FastPix account yet? The sample works without one - or start free with $25 credit (no credit card) and report on your own views instead.
Part 1: Get view data flowing from your player
Playback happens in your player. FastPix Video Data captures every interaction from inside it: it dedupes the beacons, stitches events into sessions, adds geo and device data, and computes quality scores, across millions of concurrent views when you need it. What you export is one clean row per view, which is exactly what a weekly report needs.
Already collecting views with FastPix? Skip to Part 2. New here? Setup is about ten minutes.
Step 1: Add the player (or add monitoring to the one you have)
The FastPix Player has the Video Data beacon built in:
<script src="https://unpkg.com/@fastpix/[email protected]/dist/player.js"></script>
<fastpix-player playback-id="YOUR_PLAYBACK_ID" stream-type="on-demand"></fastpix-player>Using your own player? The monitoring SDK captures the same data from hls.js, video.js, or dash.js, plus native mobile players:
import fastpixMetrix from "@fastpix/data-core";
fastpixMetrix.tracker(videoElement, {
hlsjs: hls,
Hls: Hls, // both are required
data: { workspace_id: "YOUR_WORKSPACE_KEY" }, // the only mandatory field
});Step 2: Pass who's watching through custom dimensions
Identity turns a traffic report into an engagement report. Pass your user and course IDs and every row can be grouped by learner, lesson, and course:
fastpixMetrix.tracker(videoElement, {
hlsjs: hls,
Hls: Hls,
data: {
workspace_id: "YOUR_WORKSPACE_KEY",
custom_2: `${user.id}::${user.email}`, // who's watching
custom_5: lesson.slug, // which lesson
custom_9: course.title, // which course
},
});If custom_2 comes back null in your export, the usual cause is setting metadata after the tracker initialized. Pass it inside the tracker() call.
Step 3: Export the views
Views appear in the Video Data dashboard seconds after playback. Export from Views → Export CSV, or pull the same rows from the export API - which is what your cron job will do every Monday.
Part 2: Build the report
The stack is chdb (embedded ClickHouse, pip install chdb). No warehouse needed to start; the same SQL moves to a real ClickHouse when your history outgrows CSVs.
Step 4: Load the week
import chdb, json, datetime
views = chdb.query("""
SELECT view_id, video_title,
CAST(video_duration AS Int64) AS duration_ms,
CAST(view_total_content_playback_time AS Int64) AS played_ms,
final_viewer_id, view_start, events
FROM file('fastpix-video-data-sample-export.csv', CSVWithNames)
WHERE video_duration > 0
""", "DataFrame")
def view_started_at(row):
"""First wall-clock timestamp in the event stream (robust even if
view_start was mangled by a spreadsheet round-trip)."""
try:
return datetime.datetime.fromisoformat(row["view_start"].replace("Z", "+00:00"))
except ValueError:
for e in json.loads(row["events"]):
if e.get("vt"):
return datetime.datetime.fromtimestamp(e["vt"] / 1000, datetime.timezone.utc)
views["start"] = views.apply(view_started_at, axis=1)The fallback in view_started_at is not paranoia. If a CSV has ever been opened in Excel and re-saved, the timestamp columns come back mangled (18:59.4 instead of an ISO date). The event stream's wall-clock values survive, so derive from those when parsing fails.
In production you filter to the last 7 days; for the sample, the script picks the busiest week in the file so the report has something to show.
Step 5: Compute each video's week
For every video watched this week: views, viewers, watch time, completion, the biggest drop-off point, and the most replayed section. The last two reuse the interval and jump logic from the retention graph and most replayed moments articles; both helpers are included in full in the notebook.
for title, g in this_week.groupby("video_title"):
dur = int(g["duration_ms"].iloc[0])
completion = (g["played_ms"].clip(upper=dur) / dur).mean()
watch_min = g["played_ms"].sum() / 60000
presence = presence_per_segment(g) # from the retention article
drop, at = biggest_drop(presence) # largest fall between segments
replay_at, n = most_replayed_section(g) # from the rewatch article
rows.append((title, len(g), g["final_viewer_id"].nunique(),
watch_min, completion, (drop, at), (replay_at, n)))Two report-quality guards worth copying: suppress week-over-week comparisons when the prior week has under 10 views (a "+3,200% vs last week" line helps nobody), and ignore rewatches that land in a video's first segment - those are restarts, not studied moments.
Step 6: Render it
The report is a plain HTML file: four summary tiles, one row per video, and a needs-attention list built from two rules (completion under 15% with 5+ views, or a rewatch section worth clipping). The full template is in the notebook; it's ordinary string formatting, no framework.
html = render_report(week_range, tiles, rows, attention)
open("weekly_report.html", "w").write(html)That's the report at the top of this page. Every number in it came from the sample export.
Start using FastPix for free - $25 credit, no credit card required. Your first weekly report can cover real views by next Monday. Create your workspace →
Step 7: Put it on a schedule and send it
# crontab: every Monday, 7:00
0 7 * * 1 python weekly_report.py && python send_report.py# send_report.py - swap for your email provider's API
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["Subject"] = "Video engagement report - week of May 9"
msg["From"], msg["To"] = "[email protected]", "[email protected]"
msg.set_content("This week's video engagement report is attached.")
msg.add_attachment(open("weekly_report.html").read(), subtype="html")
with smtplib.SMTP("your-smtp-host", 587) as s:
s.send_message(msg)In production, pull the export from the API in the same job instead of reading a saved CSV.
Part 3: Reading this week's sample report
Lecture 1 has a problem the view count hides. 19 views, the most of any video this week, but 1% average completion, and the biggest drop-off comes at 3:31 with 63% of the audience leaving. Whatever happens at 3:31 - in this catalog, the end of the housekeeping - is costing nearly two-thirds of viewers. That line alone justifies the report.
The workshop has a clip waiting. 6% completion overall, but viewers rewatch the section at 23:20 (3 rewatches this week). Low completion plus a studied moment means the value is buried, not absent: clip the 23:20 section with instant clipping and give it its own title.
The micro-lesson is the format working. 42% completion, no flagged drop-off. Short lessons keep holding viewers, week after week; the report keeps proving it to whoever plans the catalog.
From report to product
The report is the internal version of this data. The product version is the same numbers served to your users: an analytics page per instructor or per customer, fed by streaming exports into your own database. That architecture (store, serve, render, with custom dimensions as tenant keys) is its own article: Build video analytics into your product.
How different teams use this
| Team | Artifact | Cadence |
|---|---|---|
| Course / content team (education) | Video engagement report: retention per video published in the last 30 days | Weekly |
| B2B SaaS with video in product | Per-customer engagement report, rolled up by account via custom dimensions | Monthly / QBR |
| Media and fitness platforms | Per-episode or per-class report on day-one viewing | Daily / per release |
The B2B SaaS row deserves its own note: with course IDs and account IDs in custom dimensions, this same script renders one report per customer. That turns engagement reporting from an internal chore into a feature of your product.
Running this on your own data
Point Step 4 at your own export (or the export API) and the report fills with your catalog. The two thresholds to tune first: the completion floor in the needs-attention rules (15% fits long-form lectures; short-form catalogs should set it higher) and the rewatch minimum for clip candidates. Everything else - drop-off detection, week picking, rendering - runs unchanged.
Frequently Asked Questions (FAQs)
How do I track student engagement with videos?
Capture view-level analytics from your video player, associate each view with a learner ID, and report metrics such as completion rate, watch time, drop-off points, and replayed sections on a regular schedule. The workflow described in this article generates these insights from a single analytics export.
What should a video engagement report include?
A useful report should include total views, unique viewers, watch time, and completion rate for each video, along with audience drop-off points and the sections learners replay most often. Including a list of videos that need attention helps turn the data into actionable improvements.
How do I measure video watch time?
Measure watch time by summing the actual playback time recorded for each view instead of using the video's total duration. In a FastPix export, this value is available in the view_total_content_playback_time field, making it straightforward to calculate watch time and completion metrics.
Can I automate weekly course reports?
Yes. Schedule a recurring export, calculate engagement metrics automatically, generate an HTML report, and email it to stakeholders. The required automation can be implemented with a simple scheduled job and a short Python script.
How is this different from the reports in my LMS?
Most learning management systems report page views and course completion status, but they provide limited insight into how learners interact with videos. View-level analytics reveal where learners stop watching, which sections they replay, and how much of each video they actually complete, while giving you access to the underlying raw data.
What does it cost to start?
Getting started is free. You receive a $25 credit with no credit card required. The Video Data free tier includes 100,000 plays per month, and the FastPix Player is free for FastPix customers.






