Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Meta
- {
- "links": {
- "CH_games": "ia9i2gkbe5ok4"
- }
- }
- Params
- module.exports = {
- name: [], // выбранный игрок (селектор положит сюда ['Александр М'])
- };
- Sources
- module.exports = {
- playersSourceData: {
- qlConnectionId: Editor.getId('CH_games'),
- ui: true,
- data: {
- sql_query: `
- SELECT name
- FROM games.poker_log
- WHERE date >= toDateTime('2025-01-01 00:00:00')
- AND date < toDateTime('2026-01-01 00:00:00')
- GROUP BY name
- ORDER BY name
- LIMIT 1000001
- `,
- },
- },
- monthlySourceData: {
- qlConnectionId: Editor.getId('CH_games'),
- data: {
- sql_query: `
- SELECT
- name,
- toMonth(toDateTime(date)) AS m,
- sum(toInt64(result)) AS profit
- FROM games.poker_log
- WHERE date >= toDateTime('2025-01-01 00:00:00')
- AND date < toDateTime('2026-01-01 00:00:00')
- GROUP BY name, m
- ORDER BY name, m
- LIMIT 1000001
- `,
- },
- },
- };
- Prepare
- // Prepare.js
- function getRowsSmart(loadedData, sourceName) {
- const src = loadedData?.[sourceName];
- if (Array.isArray(src) && src.length && typeof src[0] === 'object' && !('event' in src[0])) {
- return src;
- }
- if (Array.isArray(src) && src.length && typeof src[0] === 'object' && ('event' in src[0])) {
- const columnNames =
- src.filter(i => i.event === 'metadata').map(i => i.data?.names)?.[0] || [];
- const res = [];
- src.filter(i => i.event === 'row').forEach(item => {
- const row = {};
- (item.data || []).forEach((v, i) => (row[columnNames[i]] = v));
- res.push(row);
- });
- return res;
- }
- if (src && Array.isArray(src.data)) return src.data;
- return [];
- }
- function norm(s) {
- return String(s || '').replace(/\s+/g, ' ').trim();
- }
- function toNum(v) {
- const n = Number(v);
- return Number.isFinite(n) ? n : 0;
- }
- function expand(v, k) {
- // небольшой запас по краям, чтобы подписи/столбцы не упирались
- // если v=0 → 0
- if (!Number.isFinite(v) || v === 0) return 0;
- return v > 0 ? Math.ceil(v * k) : Math.floor(v * k);
- }
- const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec','Totals'];
- const fmt = new Intl.NumberFormat('ru-RU');
- const loaded = Editor.getLoadedData() || {};
- const players = getRowsSmart(loaded, 'playersSourceData');
- const monthly = getRowsSmart(loaded, 'monthlySourceData');
- const params = (typeof Editor.getParams === 'function') ? (Editor.getParams() || {}) : {};
- let selected = norm((Array.isArray(params.name) && params.name[0]) ? params.name[0] : '');
- if (!selected && players.length) selected = norm(players[0].name);
- // 12 месяцев
- const byMonth = Array.from({ length: 12 }, () => 0);
- for (const r of monthly) {
- if (selected && norm(r.name) !== selected) continue;
- const m = toNum(r.m);
- const p = toNum(r.profit);
- if (m >= 1 && m <= 12) byMonth[m - 1] = p;
- }
- // Waterfall data
- const wf = byMonth.map((v, i) => ({ y: v, x: i }));
- wf.push({ total: true, x: 12 });
- // Считаем траекторию subtotal, чтобы правильно подобрать min/max оси
- let subtotal = 0;
- let minSubtotal = 0;
- let maxSubtotal = 0;
- for (const v of byMonth) {
- subtotal += v;
- if (subtotal < minSubtotal) minSubtotal = subtotal;
- if (subtotal > maxSubtotal) maxSubtotal = subtotal;
- }
- // Определяем режим оси
- const hasNeg = minSubtotal < 0;
- const hasPos = maxSubtotal > 0;
- let yMin = null;
- let yMax = null;
- if (hasNeg && !hasPos) {
- // только минуса
- yMax = 0;
- yMin = expand(minSubtotal, 1.02);
- } else if (!hasNeg && hasPos) {
- // только плюса
- yMin = 0;
- yMax = expand(maxSubtotal, 1.02);
- } else if (hasNeg && hasPos) {
- // и плюс и минус
- yMin = expand(minSubtotal, 1.02);
- yMax = expand(maxSubtotal, 1.02);
- } else {
- // всё 0
- yMin = -1;
- yMax = 1;
- }
- module.exports = {
- chart: {
- margin: { left: 10, right: 10, top: 10, bottom: 10 },
- spacingBottom: 35,
- },
- title: {
- text: selected ? `Profit 2025: ${selected}` : 'Profit 2025',
- },
- series: {
- data: [
- {
- type: 'waterfall',
- data: wf,
- name: 'Profit',
- dataLabels: {
- enabled: true,
- formatter: function () {
- if (!this.y) return '';
- return fmt.format(this.y);
- },
- },
- },
- ],
- },
- xAxis: {
- type: 'category',
- categories: MONTHS,
- labels: { rotation: -35 },
- },
- yAxis: [
- {
- min: yMin,
- max: yMax,
- startOnTick: true,
- endOnTick: true,
- maxPadding: 0,
- minPadding: 0,
- ticks: { pixelInterval: 120 },
- labels: {
- formatter: function () {
- return fmt.format(this.value);
- },
- },
- plotLines: [{ value: 0, width: 1 }],
- },
- ],
- };
- Controls
- // Controls.js
- function getRowsSmart(loadedData, sourceName) {
- const src = loadedData?.[sourceName];
- if (Array.isArray(src) && src.length && typeof src[0] === 'object' && !('event' in src[0])) {
- return src;
- }
- if (Array.isArray(src) && src.length && typeof src[0] === 'object' && ('event' in src[0])) {
- const columnNames =
- src.filter(i => i.event === 'metadata').map(i => i.data?.names)?.[0] || [];
- const res = [];
- src.filter(i => i.event === 'row').forEach(item => {
- const row = {};
- (item.data || []).forEach((v, i) => (row[columnNames[i]] = v));
- res.push(row);
- });
- return res;
- }
- if (src && Array.isArray(src.data)) return src.data;
- return [];
- }
- function norm(s) {
- return String(s || '').replace(/\s+/g, ' ').trim();
- }
- const loaded = Editor.getLoadedData() || {};
- const players = getRowsSmart(loaded, 'playersSourceData');
- const content = players
- .map(p => norm(p.name))
- .filter(Boolean)
- .map(n => ({ title: n, value: n })); // value обязательно строка
- module.exports = {
- controls: [
- {
- type: 'select',
- label: 'Игрок',
- labelPlacement: 'top',
- param: 'name',
- content,
- searchable: true,
- multiselect: false,
- required: false,
- updateOnChange: true, // <-- ВОТ ЭТО включает авто-применение и перерендер
- width: 280,
- },
- ],
- };
Advertisement
Add Comment
Please, Sign In to add comment