Skip to content

Commit

Permalink
introduce view tabs per year with daily/monthly data
Browse files Browse the repository at this point in the history
  • Loading branch information
stklcode committed Nov 5, 2023
1 parent 0c119d1 commit 1ad5cd6
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 37 deletions.
3 changes: 2 additions & 1 deletion css/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ body.rtl .statify-chart * {


.statify-table th,
.statify-table td:last-child {
.statify-table tr.statify-table-sum td,
.statify-table td.statify-table-sum {
font-weight: 700;
}

Expand Down
30 changes: 23 additions & 7 deletions inc/class-statify-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,25 @@ public static function get_extended( WP_REST_Request $request ): WP_REST_Respons
);
}

// Parse year, if provided.
$yr = $request->get_param( 'year' );
if ( ! empty( $yr ) ) {
$yr = intval( $yr );
if ( $yr <= 0 ) {
return new WP_REST_Response(
array( 'error' => 'invalid year' ),
400
);
}
} else {
$yr = 0;
}

// Retrieve from cache, if data is not post-specific.
$post = $request->get_param( 'post' );
$stats = false;
if ( ! $post ) {
$stats = self::from_cache( $scope );
$stats = self::from_cache( $scope, $yr );
}

if ( ! $stats ) {
Expand Down Expand Up @@ -217,12 +231,12 @@ public static function get_extended( WP_REST_Request $request ): WP_REST_Respons
$last_ym = $year_month;
}
} elseif ( 'day' === $scope ) {
$stats = Statify_Evaluation::get_views_for_all_days( $post );
$stats = Statify_Evaluation::get_views_for_all_days( $yr, $post );
}

// Update cache, if data is not post-specific.
if ( ! $post ) {
self::update_cache( $scope, $stats );
self::update_cache( $scope, $yr, $stats );
}
}

Expand All @@ -233,22 +247,24 @@ public static function get_extended( WP_REST_Request $request ): WP_REST_Respons
* Retrieve data from cache.
*
* @param string $scope Scope (year, month, day).
* @param int $index Optional index (e.g. year).
*
* @return array|false Transient data or FALSE.
*/
private static function from_cache( string $scope ) {
return get_transient( 'statify_data_' . $scope );
private static function from_cache( string $scope, int $index = 0 ) {
return get_transient( 'statify_data_' . $scope . ( $index > 0 ? '_' . $index : '' ) );
}

/**
* Update data cache.
*
* @param string $scope Scope (year, month, day).
* @param int $index Optional index (e.g. year).
* @param array $data Data.
*/
private static function update_cache( string $scope, array $data ): void {
private static function update_cache( string $scope, int $index, array $data ): void {
set_transient(
'statify_data_' . $scope,
'statify_data_' . $scope . ( $index > 0 ? '_' . $index : '' ),
$data,
30 * MINUTE_IN_SECONDS
);
Expand Down
49 changes: 25 additions & 24 deletions inc/class-statify-evaluation.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,36 +126,37 @@ public static function get_years(): array {
* Returns the views for all days.
* If the given URL is not the empty string, the result is restricted to the given post.
*
* @param string $post_url the URL of the post to select for (or the empty string for all posts).
* @param int $single_year single year.
* @param string $post_url the URL of the post to select for (or the empty string for all posts).
*
* @return array an array with date as key and views as value
*/
public static function get_views_for_all_days( string $post_url = '' ): array {
public static function get_views_for_all_days( int $single_year, string $post_url = '' ): array {
global $wpdb;

if ( empty( $post_url ) ) {
// For all posts.
$results = $wpdb->get_results(
'SELECT `created` as `date`, COUNT(`created`) as `count`' .
" FROM `$wpdb->statify`" .
' GROUP BY `created`' .
' ORDER BY `created`',
ARRAY_A
);
} else {
// Only for selected posts.
$results = $wpdb->get_results(
$wpdb->prepare(
'SELECT `created` as `date`, COUNT(`created`) as `count`' .
" FROM `$wpdb->statify`" .
' WHERE `target` = %s' .
' GROUP BY `created`' .
' ORDER BY `created`',
$post_url
),
ARRAY_A
);
$query = 'SELECT `created` as `date`, COUNT(`created`) as `count`' .
" FROM `$wpdb->statify`";
$args = array();

if ( $single_year > 0 ) {
$query .= ' WHERE YEAR(`created`) = %d';
$args[] = $single_year;
}

if ( ! empty( $post_url ) ) {
$query .= ( $single_year > 0 ? ' AND' : ' WHERE' ) . ' `target` = %s';
$args[] = $post_url;
}

$query .= ' GROUP BY `created`' .
' ORDER BY `created`';

if ( ! empty( $args ) ) {
$query = $wpdb->prepare( $query, $args );
}

$results = $wpdb->get_results( $query, ARRAY_A );

$views_for_all_days = array();
foreach ( $results as $result ) {
$views_for_all_days[ $result['date'] ] = intval( $result['count'] );
Expand Down
141 changes: 140 additions & 1 deletion js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
);
const refreshBtn = document.getElementById('statify_refresh');

const chartElemDaily = document.getElementById('statify_chart_daily');
const chartElemMonthly = document.getElementById('statify_chart_monthly');
const chartElemYearly = document.getElementById('statify_chart_yearly');
const yearlyTable = document.getElementById('statify-table-yearly');
const dailyTable = document.getElementById('statify-table-daily');

/**
* Update the dashboard widget
Expand Down Expand Up @@ -61,6 +63,22 @@
});
}

/**
* Render monthly statistics.
*
* @param {number} year Year to load data for.
*
* @return {Promise<{[key: string]: number}>} Data promise from API.
*/
function loadDaily(year) {
year = encodeURIComponent(year);

// Load data from API.
return wp.apiFetch({
path: `/statify/v1/stats/extended?scope=day&year=${year}`,
});
}

/**
* Render monthly statistics.
*
Expand All @@ -71,6 +89,19 @@
return wp.apiFetch({ path: '/statify/v1/stats/extended?scope=month' });
}

/**
* Render daily statistics.
*
* @param {HTMLElement} root Root element.
* @param {{[key: string]: number}} data Data from API.
*/
function renderDaily(root, data) {
const labels = Object.keys(data);
const values = Object.values(data);

render(root, labels, values);
}

/**
* Render monthly statistics.
*
Expand Down Expand Up @@ -308,13 +339,109 @@
}

col = document.createElement('TD');
col.classList.add('statify-table-sum');
col.innerText = sum;
row.appendChild(col);

tbody.insertBefore(row, tbody.firstChild);
}
}

/**
* Render yearly table.
*
* @param {HTMLElement} table Root element.
* @param {any} data Data from API.
*/
function renderDailyTable(table, data) {
const rows = Array.from(table.querySelectorAll('tbody > tr'));
const cols = rows.map((row) => Array.from(row.querySelectorAll('td')));
let out = cols.slice(0, 31);

const sum = Array(12).fill(0);
const vls = Array(12).fill(0);
const min = Array(12).fill(Number.MAX_SAFE_INTEGER);
const max = Array(12).fill(0);

for (const [day, count] of Object.entries(data)) {
const d = new Date(day);
const m = d.getMonth();
sum[m] += count;
++vls[m];
min[m] = Math.min(min[m], count);
max[m] = Math.max(max[m], count);
out[d.getDate() - 1][m].innerText = count;
}

out =
cols[
rows.findIndex((row) =>
row.classList.contains('statify-table-sum')
)
];
const avg =
cols[
rows.findIndex((row) =>
row.classList.contains('statify-table-avg')
)
];
for (const [m, s] of sum.entries()) {
if (vls[m] > 0) {
out[m].innerText = s;
avg[m].innerText = Math.round(s / vls[m]);
} else {
out[m].innerText = '-';
avg[m].innerText = '-';
}
}

out =
cols[
rows.findIndex((row) =>
row.classList.contains('statify-table-min')
)
];
for (const [m, s] of min.entries()) {
out[m].innerText = vls[m] > 0 ? s : '-';
}

out =
cols[
rows.findIndex((row) =>
row.classList.contains('statify-table-max')
)
];
for (const [m, s] of max.entries()) {
out[m].innerText = vls[m] > 0 ? s : '-';
}

for (const row of rows) {
row.classList.remove('placeholder');
}
}

/**
* Convert daily to monthly data.
*
* @param {{[key: string]: number}} data Daily data.
* @return {{visits: {[key: string]: {[key: string]: number}}}} Monthly data.
*/
function dailyToMonthly(data) {
const monthly = { visits: {} };
for (const [day, count] of Object.entries(data)) {
const date = new Date(day);
const year = date.getFullYear();
const month = date.getMonth();

if (!(year in monthly.visits)) {
monthly.visits[year] = {};
}
monthly.visits[year][month] =
count + (monthly.visits[year][month] || 0);
}
return monthly;
}

// Abort if config or target element is not present.
if (typeof statifyDashboard !== 'undefined') {
if (chartElem) {
Expand All @@ -332,7 +459,19 @@
updateDashboard(false);
}

if (chartElemMonthly) {
if (chartElemDaily) {
loadDaily(chartElemDaily.dataset.year).then((data) => {
renderDaily(chartElemDaily, data);

if (chartElemMonthly) {
renderMonthly(chartElemMonthly, dailyToMonthly(data));
}

if (dailyTable) {
renderDailyTable(dailyTable, data);
}
});
} else if (chartElemMonthly) {
loadMonthly()
.then((data) => {
renderMonthly(chartElemMonthly, data);
Expand Down
25 changes: 23 additions & 2 deletions tests/test-evaluation.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,45 @@ public function test_get_years() {
* Test views for all days.
*/
public function test_get_views_for_all_days() {
$this->insert_test_data( '2022-10-20', '', '/test/' );
$this->insert_test_data( '2023-03-23', '', '/', 3 );
$this->insert_test_data( '2023-03-23', '', '/test/' );
$this->insert_test_data( '2023-03-25', '', '/' );
$this->insert_test_data( '2023-03-25', '', '/test/', 2 );

self::assertSame(
array(
'2022-10-20' => 1,
'2023-03-23' => 4,
'2023-03-25' => 3,
),
Statify_Evaluation::get_views_for_all_days()
Statify_Evaluation::get_views_for_all_days(),
'unexpected results without anyfilter'
);
self::assertSame(
array(
'2022-10-20' => 1,
'2023-03-23' => 1,
'2023-03-25' => 2,
),
Statify_Evaluation::get_views_for_all_days( '/test/' )
Statify_Evaluation::get_views_for_all_days( 0, '/test/' ),
'unexpected results with post filter'
);
self::assertSame(
array(
'2023-03-23' => 4,
'2023-03-25' => 3,
),
Statify_Evaluation::get_views_for_all_days( 2023 ),
'unexpected results with year filter'
);
self::assertSame(
array(
'2023-03-23' => 1,
'2023-03-25' => 2,
),
Statify_Evaluation::get_views_for_all_days( 2023, '/test/' ),
'unexpected results with year and post filter'
);
}

Expand Down
Loading

0 comments on commit 1ad5cd6

Please sign in to comment.