<commit_before>const nest = require('depnest')
const { h, onceTrue, computed, Value, Dict, watch, watchAll, throttle } = require('mutant')
const Chart = require('chart.js')
const pull = require('pull-stream')

exports.gives = nest('contact.html.stats')

exports.needs = nest({
  'sbot.obs.connection': 'first'
})

const MINUTE = 60 * 1000
const DAY = 24 * 60 * MINUTE

const GRAPH_Y_STEP = 20
const GRAPH_Y_MIN = 20

exports.create = function (api) {
  return nest({
    'contact.html.stats': stats
  })

  function stats (feedId) {
    const minsPerStep = 60 * 24
    const scale = 90 * DAY

    const state = buildState({ api, feedId, minsPerStep, scale })
    const canvas = h('canvas', { height: 200, width: 1200, style: { height: '200px', width: '1200px' } })

    const stats = h('ContactStats', [
      canvas
    ])

    initialiseChart({ canvas, state })
    return stats
  }
}

function initialiseChart ({ canvas, state: { data, range } }) {
  var chart = new Chart(canvas.getContext('2d'), chartConfig(range))

  watch(range, ({ lower, upper }) => {
    // set horizontal scale
    chart.options.scales.xAxes[0].time.min = lower
    chart.options.scales.xAxes[0].time.max = upper
    chart.update()
  })

  watchAll([throttle(data.pub, 300), throttle(data.pri, 300), range], (dataPub, dataPri, { lower, upper }) => {
    const _dataPub = Object.keys(dataPub)
      .sort((a, b) => a < b ? -1 : +1)
      .map(ts => {
        return {
          t: Number(ts), // NOTE - might need to offset by a half-step ?
          y: dataPub[ts]
        }
      })
    const _dataPri = Object.keys(dataPri)
      .sort((a, b) => a < b ? -1 : +1)
      .map(ts => {
        return {
          t: Number(ts), // NOTE - might need to offset by a half-step ?
          y: dataPri[ts]
        }
      })

    // update chard data
    chart.data.datasets[0].data = _dataPub
    chart.data.datasets[1].data = _dataPri

    // scales the height of the graph (to the visible data)!
    const slice = _dataPub
      .filter(d => d.t >= lower && d.t < upper)
      .map(d => d.y)
      .sort((a, b) => a > b ? -1 : +1)

    var h = slice[0]
    if (!h || h < GRAPH_Y_MIN) h = GRAPH_Y_MIN // min-height
    else h = h + (GRAPH_Y_STEP - h % GRAPH_Y_STEP) // round height to multiples of GRAPH_Y_STEP
    chart.options.scales.yAxes[0].ticks.max = h

    chart.options.scales.yAxes[0].ticks.min = -h

    chart.update()
  })
}

// ///// HELPERS /////

function buildState ({ api, feedId, minsPerStep, scale }) {
  const data = {
    pub: Dict({
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE]: 0,
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE - scale]: 0
    }),
    pri: Dict({
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE]: 0,
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE - scale]: 0
    })
  }
  onceTrue(api.sbot.obs.connection, server => {
    getData({ data, server, feedId, minsPerStep, scale })
  })

  const latest = Value(toTimeBlock(Date.now(), minsPerStep))
  // start of the most recent bar
  setInterval(() => {
    latest.set(toTimeBlock(Date.now(), minsPerStep))
  }, minsPerStep / 4 * MINUTE)

  const range = computed([latest], (latest) => {
    return {
      upper: latest + minsPerStep * MINUTE,
      lower: latest + minsPerStep * MINUTE - scale
    }
  })

  return {
    data,
    range
  }
}

function getData ({ data, server, feedId, minsPerStep, scale }) {
  const upperEnd = toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE
  const lowerBound = upperEnd - scale

  const query = [
    {
      $filter: {
        timestamp: { $gte: lowerBound },
        value: {
          author: feedId
        }
      }
    }, {
      $map: {
        ts: ['timestamp'],
        content: ['value', 'content']
      }
    }
  ]

  pull(
    server.query.read({ query, live: true }),<commit_msg>fix peer activity to show _asserted publish time_ not received time!<commit_after>const nest = require('depnest')
const { h, onceTrue, computed, Value, Dict, watch, watchAll, throttle } = require('mutant')
const Chart = require('chart.js')
const pull = require('pull-stream')

exports.gives = nest('contact.html.stats')

exports.needs = nest({
  'sbot.obs.connection': 'first'
})

const MINUTE = 60 * 1000
const DAY = 24 * 60 * MINUTE

const GRAPH_Y_STEP = 20
const GRAPH_Y_MIN = 20

exports.create = function (api) {
  return nest({
    'contact.html.stats': stats
  })

  function stats (feedId) {
    const minsPerStep = 60 * 24
    const scale = 90 * DAY

    const state = buildState({ api, feedId, minsPerStep, scale })
    const canvas = h('canvas', { height: 200, width: 1200, style: { height: '200px', width: '1200px' } })

    const stats = h('ContactStats', [
      canvas
    ])

    initialiseChart({ canvas, state })
    return stats
  }
}

function initialiseChart ({ canvas, state: { data, range } }) {
  var chart = new Chart(canvas.getContext('2d'), chartConfig(range))

  watch(range, ({ lower, upper }) => {
    // set horizontal scale
    chart.options.scales.xAxes[0].time.min = lower
    chart.options.scales.xAxes[0].time.max = upper
    chart.update()
  })

  watchAll([throttle(data.pub, 300), throttle(data.pri, 300), range], (dataPub, dataPri, { lower, upper }) => {
    const _dataPub = Object.keys(dataPub)
      .sort((a, b) => a < b ? -1 : +1)
      .map(ts => {
        return {
          t: Number(ts), // NOTE - might need to offset by a half-step ?
          y: dataPub[ts]
        }
      })
    const _dataPri = Object.keys(dataPri)
      .sort((a, b) => a < b ? -1 : +1)
      .map(ts => {
        return {
          t: Number(ts), // NOTE - might need to offset by a half-step ?
          y: dataPri[ts]
        }
      })

    // update chard data
    chart.data.datasets[0].data = _dataPub
    chart.data.datasets[1].data = _dataPri

    // scales the height of the graph (to the visible data)!
    const slice = _dataPub
      .filter(d => d.t >= lower && d.t < upper)
      .map(d => d.y)
      .sort((a, b) => a > b ? -1 : +1)

    var h = slice[0]
    if (!h || h < GRAPH_Y_MIN) h = GRAPH_Y_MIN // min-height
    else h = h + (GRAPH_Y_STEP - h % GRAPH_Y_STEP) // round height to multiples of GRAPH_Y_STEP
    chart.options.scales.yAxes[0].ticks.max = h

    chart.options.scales.yAxes[0].ticks.min = -h

    chart.update()
  })
}

// ///// HELPERS /////

function buildState ({ api, feedId, minsPerStep, scale }) {
  const data = {
    pub: Dict({
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE]: 0,
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE - scale]: 0
    }),
    pri: Dict({
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE]: 0,
      [toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE - scale]: 0
    })
  }
  onceTrue(api.sbot.obs.connection, server => {
    getData({ data, server, feedId, minsPerStep, scale })
  })

  const latest = Value(toTimeBlock(Date.now(), minsPerStep))
  // start of the most recent bar
  setInterval(() => {
    latest.set(toTimeBlock(Date.now(), minsPerStep))
  }, minsPerStep / 4 * MINUTE)

  const range = computed([latest], (latest) => {
    return {
      upper: latest + minsPerStep * MINUTE,
      lower: latest + minsPerStep * MINUTE - scale
    }
  })

  return {
    data,
    range
  }
}

function getData ({ data, server, feedId, minsPerStep, scale }) {
  const upperEnd = toTimeBlock(Date.now(), minsPerStep) + minsPerStep * MINUTE
  const lowerBound = upperEnd - scale

  const query = [
    {
      $filter: {
        timestamp: { $gte: lowerBound },
        value: {
          author: feedId
        }
      }
    }, {
      $map: {
        ts: ['value', 'timestamp'],
        content: ['value', 'content']
      }
    }
  ]

  pull(
    server.query.read({ query, live: true }),
{'fraction_non_alphanumeric': 0.15478927203065135, 'fraction_numerical': 0.015070242656449554, 'mean_word_length': 2.5243024302430244, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5285564', 'n_tokens_mistral': 2870, 'n_tokens_neox': 2665, 'n_words': 683}