Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/site/app/controllers/statistics_controller.php
diff options
context:
space:
mode:
authorfligtar@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)
commit4ecaa5fdf088ad38ceebb0bd0896dd4ebfc2188a (patch)
treec2d8bc68a8751d162d783f6509d0371a3ddc5a53 /site/app/controllers/statistics_controller.php
parent57f30aef6ece8c8cb98954f89f66bc2a7fe77974 (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.php518
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;
+ }
+}
+
+?>