<commit_before>import { h, thunk } from 'snabbdom'
import { VNode, VNodeData } from 'snabbdom/vnode'
import { AnalyseData, MaybeVNode } from './interfaces';
import AnalyseCtrl from './ctrl';
import { game } from 'game';
import { defined } from 'common';
import { bind, dataIcon } from './util';

function renderRatingDiff(rd: number | undefined): VNode | undefined {
  if (rd === 0) return h('span.rp.null', '±0');
  if (rd && rd > 0) return h('span.rp.up', '+' + rd);
  if (rd && rd < 0) return h('span.rp.down', '−' + (-rd));
  return;
}

function renderPlayer(data: AnalyseData, color: Color): VNode {
  const p = game.getPlayer(data, color);
  if (p.user) return h('a.user_link.ulpt', {
    attrs: { href: '/@/' + p.user.username }
  }, [
    h('span', p.user.username),
    renderRatingDiff(p.ratingDiff)
  ]);
  return h('span', p.name || (p.ai ? 'Stockfish level ' + p.ai : 'Anonymous'));
}

const advices = [
  ['inaccuracy', 'inaccuracies', '?!'],
  ['mistake', 'mistakes', '?'],
  ['blunder', 'blunders', '??']
];

function playerTable(ctrl: AnalyseCtrl, color: Color): VNode {
  const d = ctrl.data, trans = ctrl.trans.noarg;
  const acpl = d.analysis![color].acpl;
  return h('table', {
    hook: {
      insert(vnode) {
        window.lichess.powertip.manualUserIn(vnode.elm);
      }
    }
  }, [
    h('thead', h('tr', [
      h('td', h('i.is.color-icon.' + color)),
      h('th', renderPlayer(d, color))
    ])),
    h('tbody',
      advices.map(a => {
        const nb: number = d.analysis![color][a[0]];
        const attrs: VNodeData = nb ? {
          'data-color': color,
          'data-symbol': a[2]
        } : {};
        return h('tr' + (nb ? '.symbol' : ''), { attrs }, [
          h('td', '' + nb),
          h('th', trans(a[1]))
        ]);
      }).concat(
        h('tr', [
          h('td', '' + (defined(acpl) ? acpl : '?')),
          h('th', trans('averageCentipawnLoss'))
        ])
      ))
  ])
}

function doRender(ctrl: AnalyseCtrl): VNode {
  return h('div.advice_summary', {
    hook: {
      insert: vnode => {
        $(vnode.elm as HTMLElement).on('click', 'tr.symbol', function(this: Element) {
          ctrl.jumpToGlyphSymbol($(this).data('color'), $(this).data('symbol'));
        });
      }
    }
  }, [
    playerTable(ctrl, 'white'),
    ctrl.study ? null : h('a.button.text', {
      class: { active: !!ctrl.retro },
      attrs: dataIcon('G'),
      hook: bind('click', ctrl.toggleRetro, ctrl.redraw)
    }, ctrl.trans.noarg('learnFromYourMistakes')),
    playerTable(ctrl, 'black')
  ]);
}

export function render(ctrl: AnalyseCtrl): MaybeVNode {

  if (!ctrl.data.analysis || !ctrl.showComputer() || (ctrl.study && ctrl.study.vm.toolTab() !== 'serverEval')) return;

  // don't cache until the analysis is complete!
  const buster = ctrl.data.analysis.partial ? Math.random() : '';
  const cacheKey = '' + buster + !!ctrl.retro;

  return thunk('div.advice_summary', doRender, [ctrl, cacheKey]);
}<commit_msg>bust analysis ACPL cache in study chapters<commit_after>import { h, thunk } from 'snabbdom'
import { VNode, VNodeData } from 'snabbdom/vnode'
import { AnalyseData, MaybeVNode } from './interfaces';
import AnalyseCtrl from './ctrl';
import { game } from 'game';
import { defined } from 'common';
import { bind, dataIcon } from './util';

function renderRatingDiff(rd: number | undefined): VNode | undefined {
  if (rd === 0) return h('span.rp.null', '±0');
  if (rd && rd > 0) return h('span.rp.up', '+' + rd);
  if (rd && rd < 0) return h('span.rp.down', '−' + (-rd));
  return;
}

function renderPlayer(data: AnalyseData, color: Color): VNode {
  const p = game.getPlayer(data, color);
  if (p.user) return h('a.user_link.ulpt', {
    attrs: { href: '/@/' + p.user.username }
  }, [
    h('span', p.user.username),
    renderRatingDiff(p.ratingDiff)
  ]);
  return h('span', p.name || (p.ai ? 'Stockfish level ' + p.ai : 'Anonymous'));
}

const advices = [
  ['inaccuracy', 'inaccuracies', '?!'],
  ['mistake', 'mistakes', '?'],
  ['blunder', 'blunders', '??']
];

function playerTable(ctrl: AnalyseCtrl, color: Color): VNode {
  const d = ctrl.data, trans = ctrl.trans.noarg;
  const acpl = d.analysis![color].acpl;
  return h('table', {
    hook: {
      insert(vnode) {
        window.lichess.powertip.manualUserIn(vnode.elm);
      }
    }
  }, [
    h('thead', h('tr', [
      h('td', h('i.is.color-icon.' + color)),
      h('th', renderPlayer(d, color))
    ])),
    h('tbody',
      advices.map(a => {
        const nb: number = d.analysis![color][a[0]];
        const attrs: VNodeData = nb ? {
          'data-color': color,
          'data-symbol': a[2]
        } : {};
        return h('tr' + (nb ? '.symbol' : ''), { attrs }, [
          h('td', '' + nb),
          h('th', trans(a[1]))
        ]);
      }).concat(
        h('tr', [
          h('td', '' + (defined(acpl) ? acpl : '?')),
          h('th', trans('averageCentipawnLoss'))
        ])
      ))
  ])
}

function doRender(ctrl: AnalyseCtrl): VNode {
  return h('div.advice_summary', {
    hook: {
      insert: vnode => {
        $(vnode.elm as HTMLElement).on('click', 'tr.symbol', function(this: Element) {
          ctrl.jumpToGlyphSymbol($(this).data('color'), $(this).data('symbol'));
        });
      }
    }
  }, [
    playerTable(ctrl, 'white'),
    ctrl.study ? null : h('a.button.text', {
      class: { active: !!ctrl.retro },
      attrs: dataIcon('G'),
      hook: bind('click', ctrl.toggleRetro, ctrl.redraw)
    }, ctrl.trans.noarg('learnFromYourMistakes')),
    playerTable(ctrl, 'black')
  ]);
}

export function render(ctrl: AnalyseCtrl): MaybeVNode {

  if (!ctrl.data.analysis || !ctrl.showComputer() || (ctrl.study && ctrl.study.vm.toolTab() !== 'serverEval')) return;

  // don't cache until the analysis is complete!
  const buster = ctrl.data.analysis.partial ? Math.random() : '';
  let cacheKey = '' + buster + !!ctrl.retro;
  if (ctrl.study) cacheKey += ctrl.study.data.chapter.id;

  return thunk('div.advice_summary', doRender, [ctrl, cacheKey]);
}
{'fraction_non_alphanumeric': 0.2057503739405019, 'fraction_numerical': 0.00232674090078112, 'mean_word_length': 2.935905820797907, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, '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': '5073708', 'n_tokens_mistral': 2165, 'n_tokens_neox': 2104, 'n_words': 448}