1<?php 2 3// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps 4namespace dokuwiki\plugin\statistics; 5 6/** 7 * Create the data for graph visualization 8 */ 9class StatisticsGraph 10{ 11 private \helper_plugin_statistics $hlp; 12 private string $from; 13 private string $to; 14 private int $width; 15 private int $height; 16 17 /** 18 * Initialize a new Graph 19 * 20 * @param \helper_plugin_statistics $hlp 21 * @param string $from From date 22 * @param string $to To date 23 * @param int $width width of the graph in pixels 24 * @param int $height height of the graph in pixels 25 */ 26 public function __construct(\helper_plugin_statistics $hlp, $from, $to, $width, $height) 27 { 28 $this->hlp = $hlp; 29 $this->from = $from; 30 $this->to = $to; 31 $this->width = $width; 32 $this->height = $height; 33 } 34 35 /** 36 * Create a PieChart 37 * 38 * @param array $data associative array contianing label and values 39 */ 40 protected function pieChart($data) 41 { 42 $data = [ 43 'datasets' => [ 44 [ 45 'data' => array_values($data), 46 ], 47 ], 48 'labels' => array_keys($data) 49 50 ]; 51 52 $this->printGraph('countries', 'pie', $data); 53 } 54 55 /** 56 * Build a PieChart with only the top data shown and all other summarized 57 * 58 * @param string $query The function to call on the Query object to get the data 59 * @param ?string $key The key containing the label, or null for the first non-cnt key 60 * @param int $max How many discrete values to show before summarizing under "other" 61 */ 62 public function sumUpPieChart($query, $key = null, $max = 4) 63 { 64 $result = $this->hlp->getQuery()->$query(); 65 if (!$result) return; 66 67 if ($key === null) { 68 $keys = array_keys($result[0]); 69 $key = array_shift($keys); 70 if ($key === 'cnt') { 71 $key = array_shift($keys); 72 } 73 } 74 75 $data = []; 76 $top = 0; 77 foreach ($result as $row) { 78 if ($top < $max) { 79 $data[$row[$key]] = $row['cnt']; 80 } else { 81 $data['other'] += $row['cnt']; 82 } 83 $top++; 84 } 85 $this->pieChart($data); 86 } 87 88 /** 89 * Create a history graph for the given info type 90 * 91 * @param $info 92 */ 93 protected function history($info) 94 { 95 $diff = abs(strtotime($this->from) - strtotime($this->to)); 96 $days = floor($diff / (60 * 60 * 24)); 97 if ($days > 365) { 98 $interval = 'months'; 99 } elseif ($days > 56) { 100 $interval = 'weeks'; 101 } else { 102 $interval = 'days'; 103 } 104 105 $result = $this->hlp->getQuery()->history($info, $interval); 106 107 $data = []; 108 $times = []; 109 foreach ($result as $row) { 110 $data[] = $row['cnt']; 111 if ($interval == 'months') { 112 $times[] = substr($row['time'], 0, 4) . '-' . substr($row['time'], 4, 2); 113 } elseif ($interval == 'weeks') { 114 $times[] = $row['EXTRACT(YEAR FROM dt)'] . '-' . $row['time']; // FIXME 115 } else { 116 $times[] = substr($row['time'], -5); // FIXME 117 } 118 } 119 120 $data = [ 121 'datasets' => [ 122 [ 123 'label' => $this->hlp->getLang('graph_' . $info), 124 'data' => $data, 125 ], 126 ], 127 'labels' => $times 128 ]; 129 130 $this->printGraph("history_$info", 'line', $data); 131 } 132 #region Graphbuilding functions 133 134 public function countries() 135 { 136 $this->sumUpPieChart('countries', 'country'); 137 } 138 139 public function searchengines() 140 { 141 $this->sumUpPieChart('searchengines', 'engine', 3); 142 } 143 144 public function browsers() 145 { 146 $this->sumUpPieChart('browsers', 'browser'); 147 } 148 149 public function os() 150 { 151 $this->sumUpPieChart('os', 'os'); 152 } 153 154 public function topdomain() 155 { 156 $this->sumUpPieChart('topdomain', 'domain'); 157 } 158 159 public function topuser() 160 { 161 $this->sumUpPieChart('topuser', 'user'); 162 } 163 164 public function topeditor() 165 { 166 $this->sumUpPieChart('topeditor', 'user'); 167 } 168 169 public function topgroup() 170 { 171 $this->sumUpPieChart('topgroup', 'group'); 172 } 173 174 public function topgroupedit() 175 { 176 $this->sumUpPieChart('topgroupedit', 'group'); 177 } 178 179 public function viewport() 180 { 181 $result = $this->hlp->getQuery()->viewport(); 182 $data = []; 183 184 foreach ($result as $row) { 185 $data[] = [ 186 'x' => $row['res_x'], 187 'y' => $row['res_y'], 188 'r' => floor($row['cnt'] / 10), 189 ]; 190 } 191 192 $data = [ 193 'datasets' => [ 194 [ 195 'label' => $this->hlp->getLang('viewport'), 196 'data' => $data 197 ] 198 ], 199 ]; 200 201 $this->printGraph('viewport', 'bubble', $data); 202 } 203 204 public function resolution() 205 { 206 $result = $this->hlp->getQuery()->resolution(); 207 $data = []; 208 209 foreach ($result as $row) { 210 $data[] = [ 211 'x' => $row['res_x'], 212 'y' => $row['res_y'], 213 'r' => floor($row['cnt'] / 10), 214 ]; 215 } 216 217 $data = [ 218 'datasets' => [ 219 [ 220 'label' => $this->hlp->getLang('resolution'), 221 'data' => $data 222 ] 223 ], 224 ]; 225 226 $this->printGraph('resolution', 'bubble', $data); 227 } 228 229 230 public function history_page_count() 231 { 232 $this->history('page_count'); 233 } 234 235 public function history_page_size() 236 { 237 $this->history('page_size'); 238 } 239 240 public function history_media_count() 241 { 242 $this->history('media_count'); 243 } 244 245 public function history_media_size() 246 { 247 $this->history('media_size'); 248 } 249 250 public function dashboardviews() 251 { 252 $hours = ($this->from == $this->to); 253 $result = $this->hlp->getQuery()->dashboardviews($hours); 254 $data1 = []; 255 $data2 = []; 256 $data3 = []; 257 $times = []; 258 259 foreach ($result as $time => $row) { 260 $data1[] = (int) $row['pageviews']; 261 $data2[] = (int) $row['sessions']; 262 $data3[] = (int) $row['visitors']; 263 $times[] = $time . ($hours ? 'h' : ''); 264 } 265 266 $data = [ 267 'datasets' => [ 268 [ 269 'label' => $this->hlp->getLang('graph_views'), 270 'data' => $data1, 271 ], 272 [ 273 'label' => $this->hlp->getLang('graph_sessions'), 274 'data' => $data2, 275 ], 276 [ 277 'label' => $this->hlp->getLang('graph_visitors'), 278 'data' => $data3, 279 ], 280 ], 281 'labels' => $times 282 ]; 283 284 $this->printGraph('dashboardviews', 'line', $data); 285 } 286 287 public function dashboardwiki($js = false) 288 { 289 $hours = ($this->from == $this->to); 290 $result = $this->hlp->getQuery()->dashboardwiki($hours); 291 $data1 = []; 292 $data2 = []; 293 $data3 = []; 294 $times = []; 295 296 foreach ($result as $time => $row) { 297 $data1[] = (int) ($row['E'] ?? 0); 298 $data2[] = (int) ($row['C'] ?? 0); 299 $data3[] = (int) ($row['D'] ?? 0); 300 $times[] = $time . ($hours ? 'h' : ''); 301 } 302 $data = [ 303 'datasets' => [ 304 [ 305 'label' => $this->hlp->getLang('graph_edits'), 306 'data' => $data1, 307 ], 308 [ 309 'label' => $this->hlp->getLang('graph_creates'), 310 'data' => $data2, 311 ], 312 [ 313 'label' => $this->hlp->getLang('graph_deletions'), 314 'data' => $data3, 315 ], 316 ], 317 'labels' => $times 318 ]; 319 320 $this->printGraph('dashboardwiki', 'line', $data); 321 } 322 323 /** 324 * @param string $name 325 * @param string $type 326 * @param array $data 327 * @return void 328 */ 329 protected function printGraph(string $name, string $type, array $data) 330 { 331 $json = htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8'); 332 $tpl = ' 333 <chart-component 334 width="%d" 335 height="%d" 336 name="%s" 337 type="%s" 338 data="%s"></chart-component> 339 '; 340 341 echo sprintf($tpl, $this->width, $this->height, $name, $type, $json); 342 } 343 344 #endregion Graphbuilding functions 345} 346