diff options
author | fligtar@gmail.com <fligtar@gmail.com@4eb1ac78-321c-0410-a911-ec516a8615a5> | 2007-11-27 19:13:18 (GMT) |
---|---|---|
committer | fligtar@gmail.com <fligtar@gmail.com@4eb1ac78-321c-0410-a911-ec516a8615a5> | 2007-11-27 19:13:18 (GMT) |
commit | 4ecaa5fdf088ad38ceebb0bd0896dd4ebfc2188a (patch) | |
tree | c2d8bc68a8751d162d783f6509d0371a3ddc5a53 /site/app/controllers/statistics_controller.php | |
parent | 57f30aef6ece8c8cb98954f89f66bc2a7fe77974 (diff) |
making developers and editors controllers work in php5, yay
git-svn-id: http://svn.mozilla.org/addons/trunk@8302 4eb1ac78-321c-0410-a911-ec516a8615a5
Diffstat (limited to 'site/app/controllers/statistics_controller.php')
-rw-r--r-- | site/app/controllers/statistics_controller.php | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/site/app/controllers/statistics_controller.php b/site/app/controllers/statistics_controller.php new file mode 100644 index 0000000..ab10b4b --- /dev/null +++ b/site/app/controllers/statistics_controller.php @@ -0,0 +1,518 @@ +<?php +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/e + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is addons.mozilla.org site. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Scott <fligtar@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +class StatisticsController extends AppController +{ + var $name = 'Statistics'; + var $uses = array('Addon', 'Addontype', 'Application', 'User', 'Version'); + var $components = array('Amo', 'Image'); + var $helpers = array('Html', 'Javascript', 'Localization'); + + /** + * Require login for all actions + */ + function beforeFilter() { + $this->SimpleAuth->enabled = false; + $this->SimpleAcl->enabled = false; + + // beforeFilter() is apparently called before components are initialized. Cake++ + $this->Amo->startup($this); + $this->Amo->checkLoggedIn(); + + //Clean post data + $this->Amo->clean($this->data); + + $this->layout = 'mozilla'; + $this->pageTitle = 'Statistics Dashboard'.' :: '.sprintf(_('addons_home_pagetitle'), APP_PRETTYNAME); + + $this->cssAdd = array('stats', 'jquery-ui/flora/flora.all'); + $this->publish('cssAdd', $this->cssAdd); + + $this->jsAdd = array( + 'mochikit/MochiKit.js', + 'plotkit/Base.js', + 'plotkit/Layout.js', + 'plotkit/Canvas.js', + 'plotkit/SweetCanvas.js', + 'jquery-compressed.js', + 'jquery-ui/ui.tabs.js', + 'jquery-ui/ui.calendar.js', + 'jquery-ui/jquery.dimensions.js', + 'jquery-ui/ui.dialog.js', + 'jquery-ui/ui.resizable.js', + 'jquery-ui/ui.mouse.js', + 'jquery-ui/ui.draggable.js', + 'stats.js' + ); + $this->publish('jsAdd', $this->jsAdd); + + $this->breadcrumbs = array('Statistics Dashboard' => '/statistics/index'); + $this->publish('breadcrumbs', $this->breadcrumbs); + + $this->publish('subpagetitle', 'Statistics Dashboard'); + } + + /** + * Index + */ + function index($addon_id = 0) { + // If add-on id was specified, go to its statistics + if (!empty($addon_id)) { + $this->addon($addon_id); + return; + } + // If not, figure out what to do + else { + $session = $this->Session->read('User'); + $addons = $this->Addon->getAddonsByUser($session['id']); + + // If more than one add-on, give choice + if (count($addons) > 1) { + $this->publish('addons', $addons); + } + // If only one add-on, show statistics + elseif (count($addons) == 1) { + $this->addon($addons[0]); + return; + } + // If no add-ons, go to dev cp + else { + $this->redirect('/developers/index'); + return; + } + } + } + + /** + * Add-on Statistics + */ + function addon($addon_id) { + $this->Amo->clean($addon_id); + $this->publish('addon_id', $addon_id); + + $this->set('subpagetitle', 'Add-on Statistics <sup style="font-size: 50%; font-family: Courier;">BETA</sup>'); + $this->breadcrumbs['Add-on Statistics'] = '/statistics/addon/'.$addon_id; + $this->publish('breadcrumbs', $this->breadcrumbs); + + // Make sure has permission + if (!$this->Amo->checkOwnership($addon_id)) { + $this->flash(_('devcp_error_addon_access_denied'), '/developers/index'); + return; + } + + $this->set('addon_name', $this->Addon->getAddonName($addon_id)); + $session = $this->Session->read('User'); + $this->publish('email', $session['email']); + $this->publish('all_addons', $this->Addon->getAddonsByUser($session['id'])); + + // Add-on icon + $addonIcon = $this->Image->urlForAddonIcon($addon_id); + if (empty($addonIcon)) + $addonIcon = $this->base.'/img/'.DEFAULT_ADDON_ICON; + $this->publish('addonIcon', $addonIcon); + + // Get all valid update ping dates + $updatepingDates = $this->Addon->query("SELECT DISTINCT date FROM update_counts WHERE addon_id='{$addon_id}'"); + $updateDates = array(); + if (!empty($updatepingDates)) { + foreach ($updatepingDates as $updatepingDate) { + $updateDates[] = "'{$updatepingDate['update_counts']['date']}'"; + } + } + $this->set('updateDates', $updateDates); + + $bothStart = $this->Addon->query("(SELECT date FROM download_counts WHERE addon_id={$addon_id}) UNION (SELECT date FROM update_counts WHERE addon_id={$addon_id}) ORDER BY date LIMIT 1"); + $this->publish('bothStart', $bothStart[0][0]['date']); + $bothEnd = $this->Addon->query("(SELECT date FROM download_counts WHERE addon_id={$addon_id}) UNION (SELECT date FROM update_counts WHERE addon_id={$addon_id}) ORDER BY date DESC LIMIT 1"); + $this->publish('bothEnd', $bothEnd[0][0]['date']); + + // Data for overview + $statsQry = $this->Addon->query("SELECT totaldownloads, weeklydownloads, created FROM addons WHERE id={$addon_id}"); + $stats['totaldownloads'] = $statsQry[0]['addons']['totaldownloads']; + $stats['weeklydownloads'] = $statsQry[0]['addons']['weeklydownloads']; + $stats['addon_created'] = $statsQry[0]['addons']['created']; + $statsQry = $this->Addon->query("SELECT AVG(count) FROM update_counts WHERE addon_id={$addon_id}"); + $stats['avg_updatepings'] = $statsQry[0][0]['AVG(count)']; + $statsQry = $this->Addon->query("SELECT AVG(count) FROM download_counts WHERE addon_id={$addon_id}"); + $stats['avg_downloads'] = $statsQry[0][0]['AVG(count)']; + $statsQry = $this->Addon->query("SELECT count, date FROM update_counts WHERE addon_id={$addon_id} ORDER BY date DESC LIMIT 2"); + $stats['last_updatepings'] = $statsQry[0]['update_counts']['count']; + $stats['last_updatepings_date'] = $statsQry[0]['update_counts']['date']; + $stats['previous_updatepings'] = $statsQry[1]['update_counts']['count']; + $stats['previous_updatepings_date'] = $statsQry[1]['update_counts']['date']; + $stats['updateping_change'] = (($stats['last_updatepings'] - $stats['previous_updatepings']) / $stats['previous_updatepings']) * 100; + $statsQry = $this->Addon->query("SELECT count, date FROM download_counts WHERE addon_id={$addon_id} ORDER BY date DESC LIMIT 1"); + $stats['last_downloads'] = $statsQry[0]['download_counts']['count']; + $stats['last_downloads_date'] = $statsQry[0]['download_counts']['date']; + $this->set('stats', $stats); + + $this->render('addon', 'mozilla'); + } + + /** + * JSON data + */ + function json($addon_id, $type = '', $chart = 'downloads', $subchart = '') { + $this->publish('chart', $chart); + $this->publish('subchart', $subchart); + $this->Amo->clean($addon_id); + $this->publish('addon_id', $addon_id); + + // Make sure has permission + if (!$this->Amo->checkOwnership($addon_id)) { + return; + } + + if ($type == 'historical') { + $this->_historicalChart($addon_id, $chart); + } + elseif ($type == 'breakdown') { + $this->_breakdownChart($addon_id, $chart, $subchart); + } + + define('NO_MICROTIME', true); + $this->render('json', 'ajax'); + } + + function _historicalChart($addon_id, $chart) { + $chartType = 'line'; + $xTicks = array(); + $values = array(); + $fullList = array(); + + if (!empty($_GET['start']) && !empty($_GET['end'])) { + $date = "{$_GET['start']} ~ {$_GET['end']}"; + $this->Amo->clean($_GET); + $dateQry = "AND date >= '{$_GET['start']}' AND date <= '{$_GET['end']}'"; + } + else { + $date = 'All Available Data'; + $dateQry = ''; + } + + if ($chart == 'downloads') { + $title = 'Historical Daily Downloads'; + $data = $this->Addon->query("SELECT * FROM download_counts WHERE addon_id={$addon_id} {$dateQry} ORDER BY date"); + + list($xTicks, $values) = $this->_lineChart($data, 'download_counts'); + } + elseif ($chart == 'updatepings') { + $title = 'Historical Daily Active Users'; + $data = $this->Addon->query("SELECT * FROM update_counts WHERE addon_id={$addon_id} {$dateQry} ORDER BY date"); + + list($xTicks, $values) = $this->_lineChart($data, 'update_counts'); + } + elseif ($chart == 'versions') { + $title = 'Historical Add-on Version Usage'; + $data = $this->Addon->query("SELECT version AS count, date FROM update_counts WHERE addon_id={$addon_id} {$dateQry} ORDER BY date"); + + list($xTicks, $valuesArray) = $this->_top5LineChart($data); + } + elseif ($chart == 'applications') { + $title = 'Historical Application Version Usage'; + $data = $this->Addon->query("SELECT application AS count, date FROM update_counts WHERE addon_id={$addon_id} {$dateQry} ORDER BY date"); + + list($xTicks, $valuesArray) = $this->_top5LineChart($data); + } + elseif ($chart == 'status') { + $title = 'Historical Add-on Usage Status'; + $data = $this->Addon->query("SELECT status AS count, date FROM update_counts WHERE addon_id={$addon_id} {$dateQry} ORDER BY date"); + + list($xTicks, $valuesArray) = $this->_top5LineChart($data); + } + elseif ($chart == 'os') { + $title = 'Historical Active Operating System Usage'; + $data = $this->Addon->query("SELECT os AS count, date FROM update_counts WHERE addon_id={$addon_id} {$dateQry} ORDER BY date"); + + list($xTicks, $valuesArray) = $this->_top5LineChart($data); + } + + $this->publish('chartTitle', $title); + $this->publish('type', $chartType); + $this->set('xTicks', $xTicks); + $this->publish('date', $date); + $this->set('fullList', $fullList); + if (!empty($valuesArray)) + $this->set('values', $valuesArray); + else + $this->set('values', array('Data 1' => $values)); + } + + function _breakdownChart($addon_id, $chart, $subchart) { + $chartType = 'pie'; + $xTicks = array(); + $values = array(); + $fullList = array(); + + if (empty($_GET['date'])) { + $date = $this->Addon->query("SELECT date FROM update_counts ORDER BY date DESC LIMIT 1"); + $date = $date[0]['update_counts']['date']; + } + else { + $date = $_GET['date']; + $this->Amo->clean($date); + } + + if ($chart == 'versions') { + $title = 'Versions Currently in Use'; + $data = $this->Addon->query("SELECT count, version FROM update_counts WHERE addon_id={$addon_id} AND date='{$date}'"); + + if (!empty($data)) + list($xTicks, $values, $fullList) = $this->_pieChart($data[0]['update_counts']['version'], $data[0]['update_counts']['count']); + } + elseif ($chart == 'applications') { + $title = 'Applications Currently in Use'; + $data = $this->Addon->query("SELECT count, application FROM update_counts WHERE addon_id={$addon_id} AND date='{$date}'"); + + if (!empty($data)) { + $totalCount = $data[0]['update_counts']['count']; + $data = $data[0]['update_counts']['application']; + + $subChartMenu = array(); + + $apps = unserialize($data); + $appnames = $this->Application->getGUIDList(); + + $i = 0; + $otherCount = 0; + foreach ($apps as $app => $versions) { + if (empty($app)) + continue; + + $appname = !empty($appnames[$app]) ? $appnames[$app] : 'Unknown App '.$app; + + arsort($versions); + // Only count if no subchart set or if we're on the requested subchart app + if (empty($subchart) || $subchart == $app || '{'.$subchart.'}' == $app) { + foreach ($versions as $version => $count) { + $percentage = ($count / $totalCount) * 100; + if ($percentage >= 2) { + $xTicks[$i] = '{v:'.$i.', label:"'.$appname.' '.$version.'"}'; + $values[$i] = "[{$i}, {$count}]"; + } + else + $otherCount += $count; + + $fullList[] = '{label:"'.$appname.' '.$version.'", count:'.$count.', percentage:'.round($percentage, 2).'}'; + $i++; + } + } + + // Add to subchart menu + $subChartMenu[] = '{guid: "'.str_replace(array('{', '}'), '', $app).'", name:"'.$appname.'"}'; + } + + if ($otherCount > 0) { + $xTicks[$i] = '{v:'.$i.', label:"Other"}'; + $values[$i] = "[{$i}, {$otherCount}]"; + } + + $this->set('subChartMenu', $subChartMenu); + } + } + elseif ($chart == 'status') { + $title = 'Status of Add-ons Currently in Use'; + $data = $this->Addon->query("SELECT count, status FROM update_counts WHERE addon_id={$addon_id} AND date='{$date}'"); + + if (!empty($data)) + list($xTicks, $values, $fullList) = $this->_pieChart($data[0]['update_counts']['status'], $data[0]['update_counts']['count']); + } + elseif ($chart == 'os') { + $title = 'Operating Systems Currently in Use'; + $data = $this->Addon->query("SELECT count, os FROM update_counts WHERE addon_id={$addon_id} AND date='{$date}'"); + + if (!empty($data)) + list($xTicks, $values, $fullList) = $this->_pieChart($data[0]['update_counts']['os'], $data[0]['update_counts']['count']); + } + + $this->publish('chartTitle', $title); + $this->publish('type', $chartType); + $this->set('xTicks', $xTicks); + $this->publish('date', $date); + $this->set('fullList', $fullList); + $this->set('values', array('Data 1' => $values)); + } + + function _lineChart($data, $table) { + $labelRate = ceil(count($data) / 20); + $labelNum = 1; + + foreach ($data as $k => $v) { + if ($labelNum == 1) + $label = date('n/j', strtotime($v[$table]['date'])); + else + $label = ''; + + $xTicks[$k] = '{v:'.$k.', label:"'.$label.'"}'; + $values[$k] = "[{$k}, {$v[$table]['count']}]"; + + if ($labelNum >= $labelRate) + $labelNum = 1; + else + $labelNum++; + } + + return array($xTicks, $values); + } + + function _top5LineChart($data) { + $xTicks = array(); + $values = array(); + $totalCounts = array(); + $itemsByDate = array(); + $top5 = array(); + + if (!empty($data)) { + // Get complete totals for every possible value over the date range + foreach ($data as $dateRow) { + $items = unserialize($dateRow['update_counts']['count']); + foreach ($items as $item => $count) { + if (!is_array($count)) { + if (!empty($totalCounts[$item])) + $totalCounts[$item] += $count; + else + $totalCounts[$item] = $count; + + $itemsByDate[$item][$dateRow['update_counts']['date']] = $count; + } + else { + if (empty($appNames)) + $appNames = $this->Application->getGUIDList(); + $appName = !empty($appNames[$item]) ? $appNames[$item] : 'Unknown App '.$item; + + // If an array, loop through applications to get versions + foreach ($count as $appVersion => $versionCount) { + $appItem = "{$appName} {$appVersion}"; + if (!empty($totalCounts[$appItem])) + $totalCounts[$appItem] += $versionCount; + else + $totalCounts[$appItem] = $versionCount; + + $itemsByDate[$appItem][$dateRow['update_counts']['date']] = $versionCount; + } + } + } + } + + // Sort by total count + arsort($totalCounts); + + // Pull top 5 for graph + $i = 0; + foreach ($totalCounts as $value => $totalCount) { + if ($i >= 6) + break; + + $top5[] = $value; + $values[$value] = array(); + $i++; + } + + // Add points for each date + $labelRate = ceil(count($data) / 20); + $labelNum = 1; + foreach ($data as $k => $dateRow) { + if ($labelNum == 1) + $label = date('n/j', strtotime($dateRow['update_counts']['date'])); + else + $label = ''; + + $xTicks[$k] = '{v:'.$k.', label:"'.$label.'"}'; + foreach ($top5 as $item) { + $count = !empty($itemsByDate[$item][$dateRow['update_counts']['date']]) ? $itemsByDate[$item][$dateRow['update_counts']['date']] : 0; + $values[$item][$k] = "[{$k}, {$count}]"; + } + + if ($labelNum >= $labelRate) + $labelNum = 1; + else + $labelNum++; + } + } + + return array($xTicks, $values); + } + + function _pieChart($data, $totalCount) { + $xTicks = array(); + $values = array(); + $fullList = array(); + + if (!empty($data)) { + $items = unserialize($data); + + arsort($items); + $i = 0; + $otherCount = 0; + foreach ($items as $item => $count) { + $percentage = ($count / $totalCount) * 100; + if ($percentage >= 1) { + $xTicks[$i] = '{v:'.$i.', label:"'.$item.'"}'; + $values[$i] = "[{$i}, {$count}]"; + } + else + $otherCount += $count; + + $fullList[] = '{label:"'.$item.'", count:'.$count.', percentage:'.round($percentage, 2).'}'; + $i++; + } + + if ($otherCount > 0) { + $xTicks[$i] = '{v:'.$i.', label:"Other"}'; + $values[$i] = "[{$i}, {$otherCount}]"; + } + } + + return array($xTicks, $values, $fullList); + } + + function feedback() { + $subject = 'Statistics Dashboard Feedback'; + $body = 'Feedback Type: '.$_POST['type']; + $body .= "\nE-mail: ".(!empty($_POST['email']) ? $_POST['email'] : 'Not given'); + $body .= "\nRelevant chart: ".(!empty($_POST['chart']) ? $_POST['chart'] : 'None'); + $body .= "\nComments:\n"; + $body .= $_POST['comments']; + + mail('amo-developers@mozilla.org', $subject, $body, "From: Filliam H. Muffman <nobody@mozilla.org>\r\n"); + + exit; + } +} + +?> |