DEV Community

ahmet gedik
ahmet gedik

Posted on

Building a Video Analytics Dashboard with Go and Chart.js

Analytics Across Diverse International Markets

TrendVidStream spans markets as different as UAE (Arabic, Gulf culture) and Finland (Finnish, Nordic culture). A custom Go backend lets you slice data exactly as your business needs.

Go Analytics Server

package main

import (
    "database/sql"
    "encoding/json"
    "log"
    "net/http"
    _ "github.com/mattn/go-sqlite3"
)

type Server struct{ db *sql.DB }

func main() {
    db, _ := sql.Open("sqlite3", "./data/trendvidstream.db?_journal_mode=WAL")
    defer db.Close()

    s := &Server{db: db}
    mux := http.NewServeMux()
    mux.HandleFunc("/api/analytics/views-by-region",    s.viewsByRegion)
    mux.HandleFunc("/api/analytics/trending-categories", s.trendingCategories)
    mux.HandleFunc("/api/analytics/peak-hours",          s.peakHours)
    mux.HandleFunc("/api/analytics/rtl-vs-ltr",          s.rtlVsLtr)
    log.Fatal(http.ListenAndServe(":8080", corsWrap(mux)))
}
Enter fullscreen mode Exit fullscreen mode

Views by Region with Script Type

var scriptTypes = map[string]string{
    "AE": "rtl", "FI": "latin", "CZ": "latin",
    "DK": "latin", "BE": "latin", "CH": "latin",
    "GB": "latin", "US": "latin",
}

type RegionStat struct {
    Region     string `json:"region"`
    Videos     int    `json:"video_count"`
    TotalViews int64  `json:"total_views"`
    ScriptType string `json:"script_type"`
}

func (s *Server) viewsByRegion(w http.ResponseWriter, r *http.Request) {
    rows, _ := s.db.Query(`SELECT region, COUNT(*) AS cnt, COALESCE(SUM(view_count),0) AS total
        FROM videos GROUP BY region ORDER BY total DESC`)
    defer rows.Close()
    var stats []RegionStat
    for rows.Next() {
        var st RegionStat
        rows.Scan(&st.Region, &st.Videos, &st.TotalViews)
        st.ScriptType = scriptTypes[st.Region]
        stats = append(stats, st)
    }
    json.NewEncoder(w).Encode(stats)
}
Enter fullscreen mode Exit fullscreen mode

RTL vs LTR Content Split

type ScriptSplit struct {
    RTL int64 `json:"rtl"`
    LTR int64 `json:"ltr"`
}

func (s *Server) rtlVsLtr(w http.ResponseWriter, r *http.Request) {
    row := s.db.QueryRow(`SELECT
        SUM(CASE WHEN region IN ('AE') THEN 1 ELSE 0 END) AS rtl,
        SUM(CASE WHEN region NOT IN ('AE') THEN 1 ELSE 0 END) AS ltr
        FROM videos`)
    var split ScriptSplit
    row.Scan(&split.RTL, &split.LTR)
    json.NewEncoder(w).Encode(split)
}
Enter fullscreen mode Exit fullscreen mode

Peak Hours Endpoint

func (s *Server) peakHours(w http.ResponseWriter, r *http.Request) {
    region := r.URL.Query().Get("region")
    query := `SELECT CAST(strftime('%H', fetched_at) AS INTEGER) AS hour, COUNT(*) AS cnt
        FROM videos WHERE fetched_at > datetime('now', '-7 days')`
    args := []any{}
    if region != "" {
        query += " AND region = ?"
        args = append(args, region)
    }
    query += " GROUP BY hour ORDER BY hour"

    rows, _ := s.db.Query(query, args...)
    defer rows.Close()
    type HourStat struct{ HourUTC int `json:"hour_utc"`; Count int `json:"count"` }
    var stats []HourStat
    for rows.Next() {
        var h HourStat
        rows.Scan(&h.HourUTC, &h.Count)
        stats = append(stats, h)
    }
    json.NewEncoder(w).Encode(stats)
}
Enter fullscreen mode Exit fullscreen mode

Chart.js Dashboard

const REGION = 'AE';
const BASE = 'https://analytics.trendvidstream.com';

async function loadDashboard() {
  const [regions, hours, split] = await Promise.all([
    fetch(`${BASE}/api/analytics/views-by-region`).then(r => r.json()),
    fetch(`${BASE}/api/analytics/peak-hours?region=${REGION}`).then(r => r.json()),
    fetch(`${BASE}/api/analytics/rtl-vs-ltr`).then(r => r.json()),
  ]);

  new Chart(document.getElementById('regionChart'), {
    type: 'bar',
    data: {
      labels: regions.map(r => r.region),
      datasets: [{ label: 'Total Views', data: regions.map(r => r.total_views),
        backgroundColor: regions.map(r => r.script_type === 'rtl'
          ? 'rgba(239,68,68,0.7)' : 'rgba(99,102,241,0.7)') }],
    },
  });

  new Chart(document.getElementById('scriptChart'), {
    type: 'pie',
    data: {
      labels: ['RTL (Arabic)', 'LTR (Latin)'],
      datasets: [{ data: [split.rtl, split.ltr], backgroundColor: ['#ef4444', '#6366f1'] }],
    },
  });

  const offset = REGION === 'AE' ? 4 : 0;
  new Chart(document.getElementById('peakChart'), {
    type: 'line',
    data: {
      labels: hours.map(h => `${(h.hour_utc + offset) % 24}:00 local`),
      datasets: [{ label: `Peak hours (${REGION})`, data: hours.map(h => h.count),
        tension: 0.3, fill: true }],
    },
  });
}
loadDashboard();
Enter fullscreen mode Exit fullscreen mode

On TrendVidStream, UAE traffic peaks at 20-23 local time while Finland peaks at 19-21 — a 3-hour offset that guides regional fetch scheduling.


This article is part of the Building TrendVidStream series. Check out TrendVidStream to see these techniques in action.

Top comments (0)