more stats
This commit is contained in:
parent
36c6d87cd3
commit
d12a82acdd
|
@ -7,10 +7,10 @@ import {
|
|||
setBrightness,
|
||||
getDateTimeString,
|
||||
} from '../core/utils';
|
||||
import { selectIsDarkMode } from '../store/selectors/gui';
|
||||
import { parseParagraph } from '../core/MarkdownParser';
|
||||
|
||||
|
||||
const selectStyle = (state) => state.gui.style.indexOf('dark') !== -1;
|
||||
|
||||
function ChatMessage({
|
||||
name,
|
||||
|
@ -20,9 +20,7 @@ function ChatMessage({
|
|||
ts,
|
||||
openCm,
|
||||
}) {
|
||||
const isDarkMode = useSelector(
|
||||
selectStyle,
|
||||
);
|
||||
const isDarkMode = useSelector(selectIsDarkMode);
|
||||
const refEmbed = useRef();
|
||||
|
||||
const isInfo = (name === 'info');
|
||||
|
|
|
@ -17,11 +17,24 @@ import {
|
|||
Tooltip,
|
||||
Legend,
|
||||
LineController,
|
||||
ArcElement,
|
||||
} from 'chart.js';
|
||||
import { Line } from 'react-chartjs-2';
|
||||
import { Line, Pie } from 'react-chartjs-2';
|
||||
|
||||
import { selectIsDarkMode } from '../store/selectors/gui';
|
||||
import { selectStats } from '../store/selectors/ranks';
|
||||
import { colorFromText } from '../core/utils';
|
||||
import {
|
||||
getCHistChartOpts,
|
||||
getCHistChartData,
|
||||
getOnlineStatsOpts,
|
||||
getOnlineStatsData,
|
||||
getHistChartOpts,
|
||||
getHistChartData,
|
||||
getCPieOpts,
|
||||
getCPieData,
|
||||
getPDailyStatsOpts,
|
||||
getPDailyStatsData,
|
||||
} from '../core/chartSettings';
|
||||
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
|
@ -32,88 +45,10 @@ ChartJS.register(
|
|||
Tooltip,
|
||||
Legend,
|
||||
LineController,
|
||||
// for pie chart
|
||||
ArcElement,
|
||||
);
|
||||
|
||||
const options = {
|
||||
responsive: true,
|
||||
aspectRatio: 1.2,
|
||||
color: '#e6e6e6',
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
color: '#656565',
|
||||
},
|
||||
ticks: {
|
||||
color: '#e6e6e6',
|
||||
},
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
color: '#656565',
|
||||
},
|
||||
ticks: {
|
||||
color: '#e6e6e6',
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
color: '#e6e6e6',
|
||||
text: 'Top 10 Countries [pxls / day]',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const onlineStatsOptions = {
|
||||
responsive: true,
|
||||
color: '#e6e6e6',
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
color: '#656565',
|
||||
},
|
||||
ticks: {
|
||||
color: '#e6e6e6',
|
||||
},
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
color: '#656565',
|
||||
},
|
||||
ticks: {
|
||||
color: '#e6e6e6',
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
color: '#e6e6e6',
|
||||
text: 'Players Online per full hour',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const Rankings = () => {
|
||||
const [area, setArea] = useState('total');
|
||||
const [
|
||||
|
@ -124,82 +59,80 @@ const Rankings = () => {
|
|||
onlineStats,
|
||||
cHistStats,
|
||||
histStats,
|
||||
pDailyStats,
|
||||
] = useSelector(selectStats, shallowEqual);
|
||||
const isDarkMode = useSelector(selectIsDarkMode);
|
||||
|
||||
const cHistData = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
const dataPerCountry = {};
|
||||
const labels = [];
|
||||
let ts = Date.now();
|
||||
let c = cHistStats.length;
|
||||
while (c) {
|
||||
const dAmount = cHistStats.length - c;
|
||||
c -= 1;
|
||||
// x label
|
||||
const date = new Date(ts);
|
||||
labels.unshift(`${date.getUTCMonth() + 1} / ${date.getUTCDate()}`);
|
||||
ts -= 1000 * 3600 * 24;
|
||||
// y data per country
|
||||
const dailyRanks = cHistStats[c];
|
||||
for (let i = 0; i < dailyRanks.length; i += 1) {
|
||||
const { cc, px } = dailyRanks[i];
|
||||
if (!dataPerCountry[cc]) {
|
||||
dataPerCountry[cc] = [];
|
||||
}
|
||||
const countryDat = dataPerCountry[cc];
|
||||
while (countryDat.length < dAmount) {
|
||||
countryDat.push(null);
|
||||
}
|
||||
countryDat.push(px);
|
||||
}
|
||||
}
|
||||
console.log(dataPerCountry);
|
||||
const countries = Object.keys(dataPerCountry);
|
||||
const datasets = countries.map((cc) => {
|
||||
const color = colorFromText(`${cc}${cc}${cc}${cc}${cc}`);
|
||||
return {
|
||||
label: cc,
|
||||
data: dataPerCountry[cc],
|
||||
borderColor: color,
|
||||
backgroundColor: color,
|
||||
};
|
||||
});
|
||||
return {
|
||||
labels,
|
||||
datasets,
|
||||
};
|
||||
return getCHistChartData(cHistStats);
|
||||
}, [area, cHistStats]);
|
||||
|
||||
const cHistOpts = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
return getCHistChartOpts(isDarkMode);
|
||||
}, [area, isDarkMode]);
|
||||
|
||||
const onlineData = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
const labels = [];
|
||||
const data = [];
|
||||
let ts = Date.now();
|
||||
let c = onlineStats.length;
|
||||
while (c) {
|
||||
c -= 1;
|
||||
const date = new Date(ts);
|
||||
const hours = date.getHours();
|
||||
const key = hours || `${date.getMonth() + 1} / ${date.getDate()}`;
|
||||
labels.unshift(String(key));
|
||||
ts -= 1000 * 3600;
|
||||
data.push(onlineStats[c]);
|
||||
}
|
||||
return {
|
||||
labels,
|
||||
datasets: [{
|
||||
label: 'Players',
|
||||
data,
|
||||
borderColor: '#3fadda',
|
||||
backgroundColor: '#3fadda',
|
||||
}],
|
||||
};
|
||||
return getOnlineStatsData(onlineStats);
|
||||
}, [area, onlineStats]);
|
||||
|
||||
const onlineOpts = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
return getOnlineStatsOpts(isDarkMode);
|
||||
}, [area, isDarkMode]);
|
||||
|
||||
const histData = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
return getHistChartData(histStats);
|
||||
}, [area, histStats]);
|
||||
|
||||
const histOpts = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
return getHistChartOpts(isDarkMode);
|
||||
}, [area, isDarkMode]);
|
||||
|
||||
const pDailyData = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
return getPDailyStatsData(pDailyStats);
|
||||
}, [area, pDailyStats]);
|
||||
|
||||
const pDailyOpts = useMemo(() => {
|
||||
if (area !== 'charts') {
|
||||
return null;
|
||||
}
|
||||
return getPDailyStatsOpts(isDarkMode);
|
||||
}, [area, isDarkMode]);
|
||||
|
||||
const cPieData = useMemo(() => {
|
||||
if (area !== 'countries') {
|
||||
return null;
|
||||
}
|
||||
return getCPieData(dailyCRanking);
|
||||
}, [area, dailyCRanking]);
|
||||
|
||||
const cPieOpts = useMemo(() => {
|
||||
if (area !== 'countries') {
|
||||
return null;
|
||||
}
|
||||
return getCPieOpts(isDarkMode);
|
||||
}, [area, isDarkMode]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="content">
|
||||
|
@ -210,7 +143,7 @@ const Rankings = () => {
|
|||
(area === 'total') ? 'modallink selected' : 'modallink'
|
||||
}
|
||||
onClick={() => setArea('total')}
|
||||
>{t`Total`}</span>
|
||||
> {t`Total`}</span>
|
||||
<span className="hdivider" />
|
||||
<span
|
||||
role="button"
|
||||
|
@ -219,7 +152,7 @@ const Rankings = () => {
|
|||
(area === 'today') ? 'modallink selected' : 'modallink'
|
||||
}
|
||||
onClick={() => setArea('today')}
|
||||
>{t`Today`}</span>
|
||||
> {t`Today`}</span>
|
||||
<span className="hdivider" />
|
||||
<span
|
||||
role="button"
|
||||
|
@ -228,7 +161,7 @@ const Rankings = () => {
|
|||
(area === 'yesterday') ? 'modallink selected' : 'modallink'
|
||||
}
|
||||
onClick={() => setArea('yesterday')}
|
||||
>{t`Yesterday`}</span>
|
||||
> {t`Yesterday`}</span>
|
||||
<span className="hdivider" />
|
||||
<span
|
||||
role="button"
|
||||
|
@ -237,7 +170,7 @@ const Rankings = () => {
|
|||
(area === 'countries') ? 'modallink selected' : 'modallink'
|
||||
}
|
||||
onClick={() => setArea('countries')}
|
||||
>{t`Countries Today`}</span>
|
||||
> {t`Countries Today`}</span>
|
||||
<span className="hdivider" />
|
||||
<span
|
||||
role="button"
|
||||
|
@ -246,8 +179,15 @@ const Rankings = () => {
|
|||
(area === 'charts') ? 'modallink selected' : 'modallink'
|
||||
}
|
||||
onClick={() => setArea('charts')}
|
||||
>{t`Chart`}</span>
|
||||
> {t`Charts`}</span>
|
||||
</div>
|
||||
<br />
|
||||
{(area === 'countries') && (
|
||||
<>
|
||||
<Pie options={cPieOpts} data={cPieData} />
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
{(['total', 'today', 'yesterday', 'countries'].includes(area)) && (
|
||||
<table style={{
|
||||
display: 'inline',
|
||||
|
@ -336,8 +276,10 @@ const Rankings = () => {
|
|||
)}
|
||||
{(area === 'charts') && (
|
||||
<>
|
||||
<Line options={options} data={cHistData} />
|
||||
<Line options={onlineStatsOptions} data={onlineData} />
|
||||
<Line options={cHistOpts} data={cHistData} />
|
||||
<Line options={onlineOpts} data={onlineData} />
|
||||
<Line options={pDailyOpts} data={pDailyData} />
|
||||
<Line options={histOpts} data={histData} />
|
||||
</>
|
||||
)}
|
||||
<p>
|
||||
|
|
|
@ -12,6 +12,10 @@ import {
|
|||
getCountryDailyHistory,
|
||||
getCountryRanks,
|
||||
getTopDailyHistory,
|
||||
storeHourlyPixelsPlaced,
|
||||
getHourlyPixelStats,
|
||||
getDailyPixelStats,
|
||||
populateDailyTotal,
|
||||
} from '../data/redis/ranks';
|
||||
import socketEvents from '../socket/socketEvents';
|
||||
import logger from './logger';
|
||||
|
@ -22,13 +26,24 @@ import { DailyCron, HourlyCron } from '../utils/cron';
|
|||
class Ranks {
|
||||
constructor() {
|
||||
this.ranks = {
|
||||
// ranking today of users by pixels
|
||||
dailyRanking: [],
|
||||
// ranking of users by pixels
|
||||
ranking: [],
|
||||
// ranking today of countries by pixels
|
||||
dailyCRanking: [],
|
||||
// yesterdays ranking of users by pixels
|
||||
prevTop: [],
|
||||
// online user amount by hour
|
||||
onlineStats: [],
|
||||
// ranking of countries by day
|
||||
cHistStats: [],
|
||||
// ranking of users by day
|
||||
histStats: [],
|
||||
// pixels placed by hour
|
||||
pHourlyStats: [],
|
||||
// pixels placed by day
|
||||
pDailyStats: [],
|
||||
};
|
||||
/*
|
||||
* we go through socketEvents for sharding
|
||||
|
@ -42,6 +57,7 @@ class Ranks {
|
|||
}
|
||||
|
||||
async initialize() {
|
||||
await populateDailyTotal();
|
||||
try {
|
||||
let someRanks = await Ranks.dailyUpdateRanking();
|
||||
this.ranks = {
|
||||
|
@ -95,10 +111,12 @@ class Ranks {
|
|||
const cHistStats = await getCountryDailyHistory();
|
||||
const histStats = await getTopDailyHistory();
|
||||
histStats.users = await populateRanking(histStats.users);
|
||||
const pHourlyStats = await getHourlyPixelStats();
|
||||
const ret = {
|
||||
onlineStats,
|
||||
cHistStats,
|
||||
histStats,
|
||||
pHourlyStats,
|
||||
};
|
||||
if (socketEvents.amIImportant()) {
|
||||
// only main shard sends to others
|
||||
|
@ -111,8 +129,10 @@ class Ranks {
|
|||
const prevTop = await populateRanking(
|
||||
await getPrevTop(),
|
||||
);
|
||||
const pDailyStats = await getDailyPixelStats();
|
||||
const ret = {
|
||||
prevTop,
|
||||
pDailyStats,
|
||||
};
|
||||
if (socketEvents.amIImportant()) {
|
||||
// only main shard sends to others
|
||||
|
@ -127,6 +147,7 @@ class Ranks {
|
|||
}
|
||||
const amount = socketEvents.onlineCounter.total;
|
||||
await storeOnlinUserAmount(amount);
|
||||
await storeHourlyPixelsPlaced();
|
||||
await Ranks.hourlyUpdateRanking();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
import { t } from 'ttag';
|
||||
import { colorFromText } from './utils';
|
||||
|
||||
export function getCHistChartOpts(isDarkMode) {
|
||||
const options = {
|
||||
responsive: true,
|
||||
aspectRatio: 1.2,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: t`Top 10 Countries [pxls / day]`,
|
||||
},
|
||||
},
|
||||
};
|
||||
if (isDarkMode) {
|
||||
const sColor = '#e6e6e6';
|
||||
const lColor = '#656565';
|
||||
options.color = sColor;
|
||||
options.scales.x.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.x.grid.color = lColor;
|
||||
options.scales.y.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.y.grid.color = lColor;
|
||||
options.plugins.title.color = sColor;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
export function getCHistChartData(cHistStats) {
|
||||
const dataPerCountry = {};
|
||||
const labels = [];
|
||||
let ts = Date.now();
|
||||
let c = cHistStats.length;
|
||||
while (c) {
|
||||
const dAmount = cHistStats.length - c;
|
||||
c -= 1;
|
||||
// x label
|
||||
const date = new Date(ts);
|
||||
labels.unshift(`${date.getUTCMonth() + 1} / ${date.getUTCDate()}`);
|
||||
ts -= 1000 * 3600 * 24;
|
||||
// y data per country
|
||||
const dailyRanks = cHistStats[c];
|
||||
for (let i = 0; i < dailyRanks.length; i += 1) {
|
||||
const { cc, px } = dailyRanks[i];
|
||||
if (!dataPerCountry[cc]) {
|
||||
dataPerCountry[cc] = [];
|
||||
}
|
||||
const countryDat = dataPerCountry[cc];
|
||||
while (countryDat.length < dAmount) {
|
||||
countryDat.push(null);
|
||||
}
|
||||
countryDat.push(px);
|
||||
}
|
||||
}
|
||||
const countries = Object.keys(dataPerCountry);
|
||||
const datasets = countries.map((cc) => {
|
||||
const color = colorFromText(`${cc}${cc}${cc}${cc}${cc}`);
|
||||
return {
|
||||
label: cc,
|
||||
data: dataPerCountry[cc],
|
||||
borderColor: color,
|
||||
backgroundColor: color,
|
||||
};
|
||||
});
|
||||
return {
|
||||
labels,
|
||||
datasets,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOnlineStatsOpts(isDarkMode) {
|
||||
const options = {
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: t`Players Online per full hour`,
|
||||
},
|
||||
},
|
||||
};
|
||||
if (isDarkMode) {
|
||||
const sColor = '#e6e6e6';
|
||||
const lColor = '#656565';
|
||||
options.color = sColor;
|
||||
options.scales.x.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.x.grid.color = lColor;
|
||||
options.scales.y.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.y.grid.color = lColor;
|
||||
options.plugins.title.color = sColor;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
export function getOnlineStatsData(onlineStats) {
|
||||
const labels = [];
|
||||
const data = [];
|
||||
let ts = Date.now();
|
||||
let c = onlineStats.length;
|
||||
while (c) {
|
||||
c -= 1;
|
||||
const date = new Date(ts);
|
||||
const hours = date.getHours();
|
||||
const key = hours || `${date.getMonth() + 1} / ${date.getDate()}`;
|
||||
labels.unshift(String(key));
|
||||
ts -= 1000 * 3600;
|
||||
data.push(onlineStats[c]);
|
||||
}
|
||||
return {
|
||||
labels,
|
||||
datasets: [{
|
||||
label: 'Players',
|
||||
data,
|
||||
borderColor: '#3fadda',
|
||||
backgroundColor: '#3fadda',
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
export function getHistChartOpts(isDarkMode) {
|
||||
const options = {
|
||||
responsive: true,
|
||||
aspectRatio: 1.4,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'xy',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: t`Top 10 Players [pxls / day]`,
|
||||
},
|
||||
},
|
||||
};
|
||||
if (isDarkMode) {
|
||||
const sColor = '#e6e6e6';
|
||||
const lColor = '#656565';
|
||||
options.color = sColor;
|
||||
options.scales.x.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.x.grid.color = lColor;
|
||||
options.scales.y.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.y.grid.color = lColor;
|
||||
options.plugins.title.color = sColor;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
export function getHistChartData(histStats) {
|
||||
const { users, stats } = histStats;
|
||||
const dataPerUser = {};
|
||||
users.forEach((u) => { dataPerUser[u.id] = { name: u.name, data: [] }; });
|
||||
const labels = [];
|
||||
let ts = Date.now();
|
||||
let c = stats.length;
|
||||
// skipping todays dataset
|
||||
while (c > 1) {
|
||||
const dAmount = stats.length - c;
|
||||
c -= 1;
|
||||
// x label
|
||||
ts -= 1000 * 3600 * 24;
|
||||
const date = new Date(ts);
|
||||
labels.unshift(`${date.getUTCMonth() + 1} / ${date.getUTCDate()}`);
|
||||
// y data per user
|
||||
const dailyRanks = stats[c];
|
||||
for (let i = 0; i < dailyRanks.length; i += 1) {
|
||||
const { id, px } = dailyRanks[i];
|
||||
const userDat = dataPerUser[id].data;
|
||||
while (userDat.length < dAmount) {
|
||||
userDat.push(null);
|
||||
}
|
||||
userDat.push(px);
|
||||
}
|
||||
}
|
||||
const userIds = Object.keys(dataPerUser);
|
||||
const datasets = userIds.map((id) => {
|
||||
const { name, data } = dataPerUser[id];
|
||||
const color = colorFromText(name);
|
||||
return {
|
||||
label: name,
|
||||
data,
|
||||
borderColor: color,
|
||||
backgroundColor: color,
|
||||
};
|
||||
});
|
||||
return {
|
||||
labels,
|
||||
datasets,
|
||||
};
|
||||
}
|
||||
|
||||
export function getCPieOpts(isDarkMode) {
|
||||
const options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: t`Countries by Pixels Today`,
|
||||
},
|
||||
},
|
||||
};
|
||||
if (isDarkMode) {
|
||||
const sColor = '#e6e6e6';
|
||||
options.plugins.title.color = sColor;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
export function getCPieData(dailyCRanking) {
|
||||
const labels = [];
|
||||
const data = [];
|
||||
const backgroundColor = [];
|
||||
dailyCRanking.forEach((r) => {
|
||||
const { cc, px } = r;
|
||||
labels.push(cc);
|
||||
data.push(px);
|
||||
const color = colorFromText(`${cc}${cc}${cc}${cc}${cc}`);
|
||||
backgroundColor.push(color);
|
||||
});
|
||||
return {
|
||||
labels,
|
||||
datasets: [{
|
||||
label: '# of Pixels',
|
||||
data,
|
||||
backgroundColor,
|
||||
borderWidth: 1,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
export function getPDailyStatsOpts(isDarkMode) {
|
||||
const options = {
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: t`Total Pixels placed per day`,
|
||||
},
|
||||
},
|
||||
};
|
||||
if (isDarkMode) {
|
||||
const sColor = '#e6e6e6';
|
||||
const lColor = '#656565';
|
||||
options.color = sColor;
|
||||
options.scales.x.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.x.grid.color = lColor;
|
||||
options.scales.y.ticks = {
|
||||
color: sColor,
|
||||
};
|
||||
options.scales.y.grid.color = lColor;
|
||||
options.plugins.title.color = sColor;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
export function getPDailyStatsData(pDailyStats) {
|
||||
const labels = [];
|
||||
const data = [];
|
||||
let ts = Date.now();
|
||||
let c = pDailyStats.length;
|
||||
while (c) {
|
||||
c -= 1;
|
||||
ts -= 1000 * 3600 * 24;
|
||||
const date = new Date(ts);
|
||||
labels.unshift(`${date.getUTCMonth() + 1} / ${date.getUTCDate()}`);
|
||||
data.push(pDailyStats[c]);
|
||||
}
|
||||
return {
|
||||
labels,
|
||||
datasets: [{
|
||||
label: 'Pixels',
|
||||
data,
|
||||
borderColor: '#3fadda',
|
||||
backgroundColor: '#3fadda',
|
||||
}],
|
||||
};
|
||||
}
|
|
@ -11,6 +11,9 @@ const PREV_DAY_TOP_KEY = 'prankd';
|
|||
const DAY_STATS_RANKS_KEY = 'ds';
|
||||
const CDAY_STATS_RANKS_KEY = 'cds';
|
||||
const ONLINE_CNTR_KEY = 'tonl';
|
||||
const PREV_HOURLY_PLACED_KEY = 'tmph';
|
||||
const HOURLY_PXL_CNTR_KEY = 'thpx';
|
||||
const DAILY_PXL_CNTR_KEY = 'tdpx';
|
||||
|
||||
/*
|
||||
* get pixelcount and ranking
|
||||
|
@ -116,11 +119,11 @@ export async function getPrevTop() {
|
|||
*/
|
||||
export async function storeOnlinUserAmount(amount) {
|
||||
await client.lPush(ONLINE_CNTR_KEY, String(amount));
|
||||
await client.lTrim(ONLINE_CNTR_KEY, 0, 14 * 24);
|
||||
await client.lTrim(ONLINE_CNTR_KEY, 0, 7 * 24);
|
||||
}
|
||||
|
||||
/*
|
||||
* get list of online counters
|
||||
* get list of online counters per hour
|
||||
*/
|
||||
export async function getOnlineUserStats() {
|
||||
let onlineStats = await client.lRange(ONLINE_CNTR_KEY, 0, -1);
|
||||
|
@ -128,6 +131,63 @@ export async function getOnlineUserStats() {
|
|||
return onlineStats;
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate sum of scores of zset
|
||||
* do NOT use it for large seets
|
||||
*/
|
||||
async function sumZSet(key) {
|
||||
const ranks = await client.zRangeWithScores(key, 0, -1);
|
||||
let total = 0;
|
||||
ranks.forEach((r) => { total += Number(r.score); });
|
||||
return total;
|
||||
}
|
||||
|
||||
/*
|
||||
* save hourly pixels placed by substracting
|
||||
* the current daily total pixels set with the ones of an hour ago
|
||||
*/
|
||||
export async function storeHourlyPixelsPlaced() {
|
||||
const tsNow = Date.now();
|
||||
const prevData = await client.get(PREV_HOURLY_PLACED_KEY);
|
||||
let prevTs;
|
||||
let prevSum;
|
||||
if (prevData) {
|
||||
[prevTs, prevSum] = prevData.split(',').map((z) => Number(z));
|
||||
}
|
||||
|
||||
let curSum = await sumZSet(DAILY_CRANKED_KEY);
|
||||
await client.set(PREV_HOURLY_PLACED_KEY, `${tsNow},${curSum}`);
|
||||
|
||||
if (prevTs && prevTs > tsNow - 1000 * 3600 * 1.5) {
|
||||
if (prevSum > curSum) {
|
||||
// assume day change, add amount of yesterday
|
||||
const dateKey = getDateKeyOfTs(tsNow - 1000 * 3600 * 24);
|
||||
curSum += await sumZSet(`${CDAY_STATS_RANKS_KEY}:${dateKey}`);
|
||||
}
|
||||
const hourlyPixels = curSum - prevSum;
|
||||
await client.lPush(HOURLY_PXL_CNTR_KEY, String(hourlyPixels));
|
||||
await client.lTrim(HOURLY_PXL_CNTR_KEY, 0, 7 * 24);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get list of pixels placed per hour
|
||||
*/
|
||||
export async function getHourlyPixelStats() {
|
||||
let pxlStats = await client.lRange(HOURLY_PXL_CNTR_KEY, 0, -1);
|
||||
pxlStats = pxlStats.map((s) => Number(s));
|
||||
return pxlStats;
|
||||
}
|
||||
|
||||
/*
|
||||
* get list of pixels placed per day
|
||||
*/
|
||||
export async function getDailyPixelStats() {
|
||||
let pxlStats = await client.lRange(DAILY_PXL_CNTR_KEY, 0, -1);
|
||||
pxlStats = pxlStats.map((s) => Number(s));
|
||||
return pxlStats;
|
||||
}
|
||||
|
||||
/*
|
||||
* get top 10 of daily pixels over the past days
|
||||
*/
|
||||
|
@ -167,6 +227,20 @@ export async function getTopDailyHistory() {
|
|||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* for populating past daily totals
|
||||
*/
|
||||
export async function populateDailyTotal() {
|
||||
await client.del(DAILY_PXL_CNTR_KEY);
|
||||
for (let i = 14; i > 0; i -= 1) {
|
||||
const ts = Date.now() - 1000 * 3600 * 24 * i;
|
||||
const key = `${CDAY_STATS_RANKS_KEY}:${getDateKeyOfTs(ts)}`;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const sum = await sumZSet(key);
|
||||
client.lPush(DAILY_PXL_CNTR_KEY, String(sum));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get top 10 countries over the past days
|
||||
*/
|
||||
|
@ -209,12 +283,24 @@ export async function resetDailyRanks() {
|
|||
const dateKey = getDateKeyOfTs(
|
||||
Date.now() - 1000 * 3600 * 24,
|
||||
);
|
||||
// daily user rank
|
||||
await client.rename(
|
||||
DAILY_RANKED_KEY,
|
||||
`${DAY_STATS_RANKS_KEY}:${dateKey}`,
|
||||
);
|
||||
// daily country rank
|
||||
await client.rename(
|
||||
DAILY_CRANKED_KEY,
|
||||
`${CDAY_STATS_RANKS_KEY}:${dateKey}`,
|
||||
);
|
||||
// daily pixel counter
|
||||
const sum = await sumZSet(`${CDAY_STATS_RANKS_KEY}:${dateKey}`);
|
||||
await client.lPush(DAILY_PXL_CNTR_KEY, String(sum));
|
||||
await client.lTrim(DAILY_PXL_CNTR_KEY, 0, 28);
|
||||
// purge old data
|
||||
const purgeDateKey = getDateKeyOfTs(
|
||||
Date.now() - 1000 * 3600 * 24 * 21,
|
||||
);
|
||||
await client.del(`${DAY_STATS_RANKS_KEY}:${purgeDateKey}`);
|
||||
await client.del(`${CDAY_STATS_RANKS_KEY}:${purgeDateKey}`);
|
||||
}
|
||||
|
|
|
@ -324,7 +324,7 @@ export function requestDeleteAccount(password) {
|
|||
|
||||
export function requestRankings() {
|
||||
return makeAPIGETRequest(
|
||||
'https://pixelplanet.fun/ranking',
|
||||
'/ranking',
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -279,6 +279,8 @@ export function receiveStats(
|
|||
onlineStats,
|
||||
cHistStats,
|
||||
histStats,
|
||||
pDailyStats,
|
||||
pHourlyStats,
|
||||
} = rankings;
|
||||
return {
|
||||
type: 'REC_STATS',
|
||||
|
@ -289,6 +291,8 @@ export function receiveStats(
|
|||
onlineStats,
|
||||
cHistStats,
|
||||
histStats,
|
||||
pDailyStats,
|
||||
pHourlyStats,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ const initialState = {
|
|||
prevTop: [],
|
||||
onlineStats: [],
|
||||
cHistStats: [],
|
||||
histStats: [],
|
||||
histStats: { users: [], stats: [] },
|
||||
pDailyStats: [],
|
||||
pHourlyStats: [],
|
||||
};
|
||||
|
||||
export default function ranks(
|
||||
|
@ -83,11 +85,10 @@ export default function ranks(
|
|||
onlineStats,
|
||||
cHistStats,
|
||||
histStats,
|
||||
pDailyStats,
|
||||
pHourlyStats,
|
||||
} = action;
|
||||
const lastFetch = Date.now();
|
||||
return {
|
||||
...state,
|
||||
lastFetch,
|
||||
const newStats = {
|
||||
totalRanking,
|
||||
totalDailyRanking,
|
||||
dailyCRanking,
|
||||
|
@ -95,6 +96,14 @@ export default function ranks(
|
|||
onlineStats,
|
||||
cHistStats,
|
||||
histStats,
|
||||
pDailyStats,
|
||||
pHourlyStats,
|
||||
};
|
||||
const lastFetch = Date.now();
|
||||
return {
|
||||
...state,
|
||||
lastFetch,
|
||||
...newStats,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* selectors related to gui
|
||||
*/
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
export const selectIsDarkMode = (state) => (
|
||||
state.gui.style.indexOf('dark') !== -1
|
||||
);
|
|
@ -12,4 +12,5 @@ export const selectStats = (state) => [
|
|||
state.ranks.onlineStats,
|
||||
state.ranks.cHistStats,
|
||||
state.ranks.histStats,
|
||||
state.ranks.pDailyStats,
|
||||
];
|
||||
|
|
|
@ -15,7 +15,7 @@ import canvas from './reducers/canvas';
|
|||
import chat from './reducers/chat';
|
||||
import fetching from './reducers/fetching';
|
||||
|
||||
export const CURRENT_VERSION = 12;
|
||||
export const CURRENT_VERSION = 14;
|
||||
|
||||
export const migrate = (state, version) => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
|
|
Loading…
Reference in New Issue