File

libs/video-stat-dashboard/src/services/views-breakdown.service.ts

Index

Properties

Constructor

constructor(value: any, count: number)
Parameters :
Name Type Optional
value any no
count number no

Properties

Public count
count: number
Type : number
height
height: number
Type : number
Public value
value: any
Type : any
width
width: number
Type : number
x
x: number
Type : number
y
y: number
Type : number
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { countBy, forEach, maxBy } from 'lodash';
import * as moment from 'moment';
import { combineLatest } from 'rxjs/observable/combineLatest'

import { ageRanges } from '@enterprise-example/age-range';

import { VideoStatsState } from '../+state/video-stats.interfaces';
import { View, ViewsFilterState } from '../+state/video-stats.interfaces';
import { DashboardService } from './dashboard.service';
import { ViewsFilterService } from './views-filter.service';

const GRAPH_HEIGHT = 200;
const GRAPH_WIDTH = 500;

export class Rect {
  x: number;
  y: number;
  width: number;
  height: number;

  constructor(public value: any, public count: number) { }
}

export class GraphData {
  rectList: Rect[];
  width: number;
  height: number;

  constructor(public xAxis: string, views: View[], viewsFilter: ViewsFilterState) {
    this.height = GRAPH_HEIGHT;
    this.width = GRAPH_WIDTH;
    this.rectList = this.calcRectList(views, viewsFilter);
  }

  // In order to create the bars in the bar chart we need to perform the following
  // 1. Filter any values based on user input
  // 2. Identify all of the unique values associated with the xAxis
  // 3. Count the number of views associated with each values
  // 4. Determine the largest of these categories
  // 5. Scale all counts based on largest and the graph height
  // 6. Divide the width of the graph amongst the number of unique values
  // 7. Assign widths and x positions accordingly
  calcRectList(views: View[], viewsFilter: ViewsFilterState) {
    const rects: Rect[] = [];
    const filteredViews = filterViews(views, viewsFilter);
    if (filteredViews.length) {
      const groups = countBy(filteredViews, this.xAxis);
      forEach(groups, (value, key) => {
        rects.push(new Rect(key, value));
      });
      // rects now have value and count
      const maxValue = maxBy(rects, 'count').count;
      const width = (GRAPH_WIDTH / rects.length) - 1;
      rects.forEach((rect, index) => {
        rect.height = calcHeight(maxValue, rect.count, GRAPH_HEIGHT);
        rect.y = GRAPH_HEIGHT - rect.height;
        rect.width = width;
        rect.x = index * width;
      });
    }
    return rects;
  }
}

@Injectable()
export class ViewsBreakdownService {

  constructor(private store: Store<VideoStatsState>,
    private dashboardService: DashboardService,
    private viewsFilterService: ViewsFilterService) { }

  getBreakdowns() {
    return this.store.select(state => state.videoStats.viewsBreakdown
      && state.videoStats.viewsBreakdown.selectedAxis);
  }

  getGraphData() {
    const currentVideo = this.dashboardService.currentVideo;
    const viewsFilter = this.viewsFilterService.filterState;
    return combineLatest(currentVideo, this.getBreakdowns(), viewsFilter,
      (cv, breakdowns, viewsFilterState) => {
        const graphs: GraphData[] = [];
        if (cv && cv.viewDetails) {
          breakdowns.forEach(breakDown => graphs.push(new GraphData(breakDown, cv.viewDetails, viewsFilterState)));
        }
        return graphs;
      });
  }

  addNewGraphAxis(axis: string) {
    this.store.dispatch({ type: 'ADD_NEW_GRAPH_AXIS', payload: axis });
  }

  removeGroup(group: number) {
    this.store.dispatch({ type: 'TOGGLE_AGE', payload: group });
  }
}

function calcHeight(maxValue: number, value: number, graphHeight: number) {
  return Math.floor(value / maxValue * graphHeight);
}

function filterViews(views: View[], viewsFilter: ViewsFilterState) {
  const fromDate = moment(viewsFilter.dateFrom);
  const toDate = moment(viewsFilter.dateTo);

  const filteredResults = views.filter(view => {
    // Age range
    if (!ageRanges.find((range, index) =>
      view.age >= range.lower && view.age < range.upper && viewsFilter.ageRanges[index])) {
      return false;
    }

    // Check the region
    if (viewsFilter.region !== view.region && viewsFilter.region !== 'All') {
      return false;
    }

    // Check the view date
    const videoDate = moment(view.date);
    return videoDate.isBetween(fromDate, toDate);
  });
  return filteredResults;
}

results matching ""

    No results matching ""