Reworked the database. we now have "question sets" which will allow us versioning

This commit is contained in:
wclouser%mozilla.com 2006-06-29 21:41:14 +00:00
Родитель 11b41fa276
Коммит ab4df73b13
10 изменённых файлов: 398 добавлений и 112 удалений

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -11,7 +11,7 @@ class ResultsController extends AppController {
* Model's this controller uses * Model's this controller uses
* @var array * @var array
*/ */
var $uses = array('Application','Result','Intention','Issue'); var $uses = array('Application','Collection','Choice','Result');
/** /**
* Cake Helpers * Cake Helpers
@ -62,8 +62,7 @@ class ResultsController extends AppController {
$_input_name = $this->Sanitize->Sql(isset($this->data['application'][0]) ? $this->data['application'][0] : (isset($this->params['url']['application']) ? $this->params['url']['application'] : '')); $_input_name = $this->Sanitize->Sql(isset($this->data['application'][0]) ? $this->data['application'][0] : (isset($this->params['url']['application']) ? $this->params['url']['application'] : ''));
$_input_ua = $this->Sanitize->Sql(isset($this->data['ua'][0]) ? $this->data['ua'][0] : (isset($this->params['url']['ua']) ? $this->params['url']['ua'] : '')); $_input_ua = $this->Sanitize->Sql(isset($this->data['ua'][0]) ? $this->data['ua'][0] : (isset($this->params['url']['ua']) ? $this->params['url']['ua'] : ''));
// Please, oh please can we talk about standards in the future. :) The ua // The ua comes over $_GET in the form:
// comes over $_GET in the form:
// x.x.x.x (aa-bb) // x.x.x.x (aa-bb)
// Where x is the versions and a and b are locale information. We're not // Where x is the versions and a and b are locale information. We're not
// interested in the locale information, // interested in the locale information,
@ -83,7 +82,6 @@ class ResultsController extends AppController {
$app_id = $app->getLastInsertID(); $app_id = $app->getLastInsertID();
// Warning: hard coding ahead! - Hopefully this is a temporary thing.
// The database will handle any combination of questions // The database will handle any combination of questions
// (issues/intentions) and applications+versions. However, since we're // (issues/intentions) and applications+versions. However, since we're
// adding stuff in that comes in over the URL, we kinda have to guess at // adding stuff in that comes in over the URL, we kinda have to guess at
@ -92,18 +90,18 @@ class ResultsController extends AppController {
// then manually adding those values to the table. // then manually adding those values to the table.
if (stristr($this->params['url']['application'], 'Firefox') !== false) { if (stristr($this->params['url']['application'], 'Firefox') !== false) {
// Intention Id's // Intention Id's
$this->Intention->query("INSERT INTO applications_intentions VALUES ({$app_id}, 1), ({$app_id}, 2), ({$app_id}, 3), ({$app_id}, 9)"); $this->Application->query("INSERT INTO applications_collections VALUES ({$app_id}, ".DEFAULT_FIREFOX_INTENTION_SET_ID.")");
// Issue Id's // Issue Id's
$this->Issue->query("INSERT INTO applications_issues VALUES ({$app_id}, 1), ({$app_id}, 2), ({$app_id}, 3), ({$app_id}, 4), ({$app_id}, 5), ({$app_id}, 6), ({$app_id}, 7), ({$app_id}, 8), ({$app_id}, 9), ({$app_id}, 15)"); $this->Application->query("INSERT INTO applications_collections VALUES ({$app_id}, ".DEFAULT_FIREFOX_ISSUE_SET_ID.")");
} elseif (stristr($this->params['url']['application'], 'Thunderbird') !== false) { } elseif (stristr($this->params['url']['application'], 'Thunderbird') !== false) {
// Intention Id's // Intention Id's
$this->Intention->query("INSERT INTO applications_intentions VALUES ({$app_id}, 5), ({$app_id}, 6), ({$app_id}, 7), ({$app_id}, 8), ({$app_id}, 9)"); $this->Application->query("INSERT INTO applications_collections VALUES ({$app_id}, ".DEFAULT_THUNDERBIRD_INTENTION_SET_ID.")");
// Issue Id's // Issue Id's
$this->Issue->query("INSERT INTO applications_issues VALUES ({$app_id}, 10), ({$app_id}, 11), ({$app_id}, 12), ({$app_id}, 13), ({$app_id}, 14), ({$app_id}, 15)"); $this->Application->query("INSERT INTO applications_collections VALUES ({$app_id}, ".DEFAULT_THUNDERBIRD_ISSUE_SET_ID.")");
} else { } else {
// Whatever they entered doesn't have firefox or thunderbird in it. // Whatever they entered doesn't have firefox or thunderbird in it.
@ -118,13 +116,12 @@ class ResultsController extends AppController {
$_conditions = "name LIKE '{$_input_name}' AND version LIKE '{$_input_ua}' AND 1=1"; $_conditions = "name LIKE '{$_input_name}' AND version LIKE '{$_input_ua}' AND 1=1";
$_application = $this->Application->findAll($_conditions); $_application = $this->Application->findAll($_conditions);
} }
// Pull the information for our radio buttons (only the // Pull the information for our radio buttons (only the
// questions for their applications will be shown) // questions for their applications will be shown)
$this->set('intentions', $this->Intention->Application->findById($_application[0]['Application']['id'])); $this->set('intentions', $this->Application->getIntentions($this->Sanitize->sql($_application[0]['Application']['id'])));
// Checkboxes // Checkboxes
$this->set('issues', $this->Issue->Application->findById($_application[0]['Application']['id'])); $this->set('issues', $this->Application->getIssues($this->Sanitize->sql($_application[0]['Application']['id'])));
// We'll need the url parameters to put in hidden fields // We'll need the url parameters to put in hidden fields
$this->set('url_params', $this->Sanitize->html($this->params['url'])); $this->set('url_params', $this->Sanitize->html($this->params['url']));
@ -166,6 +163,9 @@ class ResultsController extends AppController {
// We'll need to include the graphing libraries // We'll need to include the graphing libraries
$this->set('include_graph_libraries', true); $this->set('include_graph_libraries', true);
// Fill in our question sets
$this->set('collections',$this->Application->getCollectionsFromUrl($this->params['url'],'issue'));
// Core data to show on page // Core data to show on page
$this->set('descriptionAndTotalsData',$this->Result->getDescriptionAndTotalsData($this->params['url'])); $this->set('descriptionAndTotalsData',$this->Result->getDescriptionAndTotalsData($this->params['url']));
} }
@ -215,8 +215,17 @@ class ResultsController extends AppController {
// Get rid of the header/footer/etc. // Get rid of the header/footer/etc.
$this->layout = null; $this->layout = null;
// Auto generated .csv's are turned off since they were taking too much
// cpu/ram. If you turn them back on, be sure to check the code - there was
// a substantial database change between the time they were disabled and now.
return false;
$csv = new csv();
$csv->loadDataFromArray($this->Result->getCsvExportData($this->params['url'], false));
// Our CSV library sends headers and everything. Keep the view empty! // Our CSV library sends headers and everything. Keep the view empty!
csv_send_csv($this->Result->getCsvExportData($this->params['url'])); $csv->sendCSV();
// I'm not exiting here in case someone is going to use post callback stuff. // I'm not exiting here in case someone is going to use post callback stuff.
// In development, that means extra lines get added to our CSVs, but in // In development, that means extra lines get added to our CSVs, but in

Просмотреть файл

@ -1,4 +1,5 @@
<?php <?php
uses('Sanitize');
class Application extends AppModel { class Application extends AppModel {
var $name = 'Application'; var $name = 'Application';
@ -10,14 +11,177 @@ class Application extends AppModel {
//var $hasOne = array('Result'); //var $hasOne = array('Result');
var $hasAndBelongsToMany = array( var $hasAndBelongsToMany = array(
'Intention' => array( 'Collection' => array( 'className' => 'Collection')
'className' => 'Intention',
'order' => 'pos'
),
'Issue' => array(
'className' => 'Issue',
'order' => 'pos'
)
); );
var $Sanitize;
function Application() {
parent::AppModel();
$this->Sanitize = new Sanitize();
}
/**
* @param int application id
* @return array set of intentions
*/
function getIntentions($id) {
// this should never happen...
if (!is_numeric($id)) {
return array();
}
$_query = "
SELECT
*
FROM
applications
JOIN applications_collections ON application_id = applications.id
JOIN collections ON collections.id = applications_collections.collection_id
JOIN choices_collections ON choices_collections.collection_id = collections.id
JOIN choices ON choices.id = choices_collections.choice_id
WHERE
applications.id={$id}
AND
choices.type='intention'";
return $this->query($_query);
}
/**
* @param int application id
* @return array set of issues
*/
function getIssues($id)
{
// this should never happen...
if (!is_numeric($id)) {
return array();
}
$_max_id = $this->getMaxCollectionId($id,'issue');
if (!is_numeric($_max_id[0][0]['max'])) {
return array();
}
$_query = "
SELECT
*
FROM
applications
JOIN applications_collections ON application_id = applications.id
JOIN collections ON collections.id = applications_collections.collection_id
JOIN choices_collections ON choices_collections.collection_id = collections.id
JOIN choices ON choices.id = choices_collections.choice_id
WHERE
applications.id={$id}
AND
choices.type='issue'
AND
collections.id={$_max_id[0][0]['max']}
";
return $this->query($_query);
}
function getMaxCollectionId($id, $type)
{
if (!is_numeric($id)) {
return false;
}
$_type = $this->Sanitize->sql($type);
$_query = "
SELECT
MAX(collections.id) as max
FROM
applications
JOIN applications_collections ON application_id = applications.id
JOIN collections ON collections.id = applications_collections.collection_id
JOIN choices_collections ON choices_collections.collection_id = collections.id
JOIN choices ON choices.id = choices_collections.choice_id
WHERE
applications.id={$id}
AND
choices.type='{$_type}'
";
return $this->query($_query);
}
/**
* @param array cake url array
* @return array set of collections
*/
function getCollectionsFromUrl($params, $type)
{
// Clean parameters for inserting into SQL
$params = $this->cleanArrayForSql($params);
$_conditions = "1=1";
// Firstly, determine our application
if (!empty($params['product'])) {
// product's come in looking like:
// Mozilla Firefox 1.5.0.1
$_exp = explode(' ',urldecode($params['product']));
if(count($_exp) == 3) {
$_product = $_exp[0].' '.$_exp[1];
$_version = $_exp[2];
$_conditions .= " AND `Application`.`name` LIKE '{$_product}'";
$_conditions .= " AND `Application`.`version` LIKE '{$_version}'";
} else {
// defaults I guess?
$_conditions .= " AND `Application`.`name` LIKE '".DEFAULT_APP_NAME."'";
$_conditions .= " AND `Application`.`version` LIKE '".DEFAULT_APP_VERSION."'";
}
} else {
// I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired
$_conditions .= " AND `Application`.`name` LIKE '".DEFAULT_APP_NAME."'";
$_conditions .= " AND `Application`.`version` LIKE '".DEFAULT_APP_VERSION."'";
}
$_application_id = $this->findAll($_conditions, 'Application.id');
return $this->getCollections($_application_id[0]['Application']['id'], $type);
}
/**
* @param int application id
* @param string choice type (either 'issue' or 'intention' right now)
* @return array set of collections
*/
function getCollections($id, $type)
{
// this should never happen...
if (!is_numeric($id)) {
return array();
}
$_type = $this->Sanitize->sql($type);
$_query = "
SELECT
*
FROM
applications
JOIN applications_collections ON application_id = applications.id
JOIN collections ON collections.id = applications_collections.collection_id
JOIN choices_collections ON choices_collections.collection_id = collections.id
JOIN choices ON choices.id = choices_collections.choice_id
WHERE
applications.id={$id}
AND
choices.type='{$_type}'
GROUP BY collections.description
ORDER BY collections.id DESC";
return $this->query($_query);
}
} }
?> ?>

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -2,17 +2,22 @@
class Result extends AppModel { class Result extends AppModel {
var $name = 'Result'; var $name = 'Result';
var $belongsTo = array('Application', 'Intention'); var $belongsTo = array('Application');
var $hasAndBelongsToMany = array('Issue' => var $hasAndBelongsToMany = array('Choice' =>
array('className' => 'Issue', array('className' => 'Choice',
'joinTable' => 'issues_results',
'foreignKey'=> 'issue_id',
'assocationForeignKey'=>'result_id',
'uniq' => true 'uniq' => true
) )
); );
var $Sanitize;
function Result()
{
parent::appModel();
$this->Sanitize = new Sanitize();
}
/** /**
* Count's all the comments, according to the parameters. * Count's all the comments, according to the parameters.
* @param array URL parameters * @param array URL parameters
@ -59,15 +64,15 @@ class Result extends AppModel {
array_push($_conditions, "`Application`.`version` LIKE '{$_version}'"); array_push($_conditions, "`Application`.`version` LIKE '{$_version}'");
} else { } else {
// defaults I guess? // defaults I guess?
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'"); array_push($_conditions, "`Application`.`name` LIKE '".DEFAULT_APP_NAME."'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'"); array_push($_conditions, "`Application`.`version` LIKE '".DEFAULT_APP_VERSION."'");
} }
} else { } else {
// I'm providing a default here, because otherwise all results will be // I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired // returned (across all applications) and that is not desired
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'"); array_push($_conditions, "`Application`.`name` LIKE '".DEFAULT_APP_NAME."'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'"); array_push($_conditions, "`Application`.`version` LIKE '".DEFAULT_APP_VERSION."'");
} }
// Do the actual query // Do the actual query
@ -124,34 +129,43 @@ class Result extends AppModel {
array_push($_conditions, "`Application`.`version` LIKE '{$_version}'"); array_push($_conditions, "`Application`.`version` LIKE '{$_version}'");
} else { } else {
// defaults I guess? // defaults I guess?
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'"); array_push($_conditions, "`Application`.`name` LIKE '".DEFAULT_APP_NAME."'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'"); array_push($_conditions, "`Application`.`version` LIKE '".DEFAULT_APP_VERSION."'");
} }
} else { } else {
// I'm providing a default here, because otherwise all results will be // I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired // returned (across all applications) and that is not desired
array_push($_conditions, "`Application`.`name` LIKE 'Mozilla Firefox'"); array_push($_conditions, "`Application`.`name` LIKE '".DEFAULT_APP_NAME."'");
array_push($_conditions, "`Application`.`version` LIKE '1.5'"); array_push($_conditions, "`Application`.`version` LIKE '".DEFAULT_APP_VERSION."'");
} }
// Save ourselves quite a few joins // Save ourselves quite a few joins
$this->unBindModel(array('hasAndBelongsToMany' => array('Issue')));
$comments = $this->findAll($_conditions, null, $pagination['order'], $pagination['show'], $pagination['page']); $comments = $this->findAll($_conditions, null, $pagination['order'], $pagination['show'], $pagination['page']);
if ($privacy) { if ($privacy) {
// Pull out all the email addresses and phone numbers // Pull out all the email addresses and phone numbers. The original
// lines are below, but commented out for the sake of speed.
// preg_replace() will replace a single level of an array according to a
// pattern. This behavior doesn't seem to be documented (at this time), so I'm not sure
// if they are going to "fix" it later. If they do, you can replace the
// current code with the commented ones, but realize it will take about
// twice as long.
foreach ($comments as $var => $val) { foreach ($comments as $var => $val) {
// Handle foo@bar.com // Handle foo@bar.com
$_email_regex = '/\ ?(.+)?@(.+)?\.(.+)?\ ?/'; $_email_regex = '/\ ?(.+)?@(.+)?\.(.+)?\ ?/';
$comments[$var]['Result']['comments'] = preg_replace($_email_regex,'$1@****.$3',$comments[$var]['Result']['comments']); $comments[$var]['Result'] = preg_replace($_email_regex,'$1@****.$3',$comments[$var]['Result']);
$comments[$var]['Result']['intention_text'] = preg_replace($_email_regex,'$1@****.$3',$comments[$var]['Result']['intention_text']);
//$comments[$var]['Result']['comments'] = preg_replace($_email_regex,'$1@****.$3',$comments[$var]['Result']['comments']);
//$comments[$var]['Result']['intention_text'] = preg_replace($_email_regex,'$1@****.$3',$comments[$var]['Result']['intention_text']);
// Handle xxx-xxx-xxxx // Handle xxx-xxx-xxxx
$_phone_regex = '/([0-9]{3})[ .-]?[0-9]{4}/'; $_phone_regex = '/([0-9]{3})[ .-]?[0-9]{4}/';
$comments[$var]['Result']['comments'] = preg_replace($_phone_regex,'$1-****',$comments[$var]['Result']['comments']); $comments[$var]['Result'] = preg_replace($_phone_regex,'$1-****',$comments[$var]['Result']);
$comments[$var]['Result']['intention_text'] = preg_replace($_phone_regex,'$1-****',$comments[$var]['Result']['intention_text']);
//$comments[$var]['Result']['comments'] = preg_replace($_phone_regex,'$1-****',$comments[$var]['Result']['comments']);
//$comments[$var]['Result']['intention_text'] = preg_replace($_phone_regex,'$1-****',$comments[$var]['Result']['intention_text']);
} }
} }
@ -220,14 +234,14 @@ class Result extends AppModel {
$_query .= " AND `applications`.`version` LIKE '{$_version}'"; $_query .= " AND `applications`.`version` LIKE '{$_version}'";
} else { } else {
// defaults I guess? // defaults I guess?
$_query .= " AND `applications`.`name` LIKE 'Mozilla Firefox'"; $_query .= " AND `applications`.`name` LIKE '".DEFAULT_APP_NAME."'";
$_query .= " AND `applications`.`version` LIKE '1.5'"; $_query .= " AND `applications`.`version` LIKE '".DEFAULT_APP_VERSION."'";
} }
} else { } else {
// I'm providing a default here, because otherwise all results will be // I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired // returned (across all applications) and that is not desired
$_query .= " AND `applications`.`name` LIKE 'Mozilla Firefox'"; $_query .= " AND `applications`.`name` LIKE '".DEFAULT_APP_NAME."'";
$_query .= " AND `applications`.`version` LIKE '1.5'"; $_query .= " AND `applications`.`version` LIKE '".DEFAULT_APP_VERSION."'";
} }
$_query .= " ORDER BY `results`.`created` ASC"; $_query .= " ORDER BY `results`.`created` ASC";
@ -236,7 +250,6 @@ class Result extends AppModel {
// Since we're exporting to a CSV, we need to flatten the results into a 2 // Since we're exporting to a CSV, we need to flatten the results into a 2
// dimensional table array // dimensional table array
$newdata = array();
foreach ($res as $result) { foreach ($res as $result) {
@ -244,18 +257,28 @@ class Result extends AppModel {
} }
if ($privacy) { if ($privacy) {
// Pull out all the email addresses and phone numbers // Pull out all the email addresses and phone numbers. The original
// lines are below, but commented out for the sake of speed.
// preg_replace() will replace a single level of an array according to a
// pattern. This behavior doesn't seem to be documented (at this time), so I'm not sure
// if they are going to "fix" it later. If they do, you can replace the
// current code with the commented ones, but realize it will take about
// twice as long.
foreach ($newdata as $var => $val) { foreach ($newdata as $var => $val) {
// Handle foo@bar.com // Handle foo@bar.com
$_email_regex = '/\ ?(.+)?@(.+)?\.(.+)?\ ?/'; $_email_regex = '/\ ?(.+)?@(.+)?\.(.+)?\ ?/';
$newdata[$var]['comments'] = preg_replace($_email_regex,'$1@****.$3',$newdata[$var]['comments']); $newdata[$var] = preg_replace($_email_regex,'$1@****.$3',$newdata[$var]);
$newdata[$var]['intention_other'] = preg_replace($_email_regex,'$1@****.$3',$newdata[$var]['intention_other']);
//$newdata[$var]['comments'] = preg_replace($_email_regex,'$1@****.$3',$newdata[$var]['comments']);
//$newdata[$var]['intention_other'] = preg_replace($_email_regex,'$1@****.$3',$newdata[$var]['intention_other']);
// Handle xxx-xxx-xxxx // Handle xxx-xxx-xxxx
$_phone_regex = '/([0-9]{3})[ .-]?[0-9]{4}/'; $_phone_regex = '/([0-9]{3})[ .-]?[0-9]{4}/';
$newdata[$var]['comments'] = preg_replace($_phone_regex,'$1-****',$newdata[$var]['comments']); $newdata[$var] = preg_replace($_phone_regex,'$1-****',$newdata[$var]);
$newdata[$var]['intention_other'] = preg_replace($_phone_regex,'$1-****',$newdata[$var]['intention_other']);
//$newdata[$var]['comments'] = preg_replace($_phone_regex,'$1-****',$newdata[$var]['comments']);
//$newdata[$var]['intention_other'] = preg_replace($_phone_regex,'$1-****',$newdata[$var]['intention_other']);
} }
} }
@ -281,7 +304,7 @@ class Result extends AppModel {
/* Below is the original query for this function. It was beautiful and /* Below is the original query for this function. It was beautiful and
* brought back just what we needed. However, it took 5.5s to run with 43000 * brought back just what we needed. However, it took 5.5s to run with 43000
* results, and since that number is just going up, it won't due to have a * results, and since that number is just going up, it won't do to have a
* query take that long (especially one on the front page). * query take that long (especially one on the front page).
//It would be nice to drop something like this in the SELECT: //It would be nice to drop something like this in the SELECT:
@ -323,34 +346,56 @@ class Result extends AppModel {
$_conditions .= " AND `Application`.`version` LIKE '{$_version}'"; $_conditions .= " AND `Application`.`version` LIKE '{$_version}'";
} else { } else {
// defaults I guess? // defaults I guess?
$_conditions .= " AND `Application`.`name` LIKE 'Mozilla Firefox'"; $_conditions .= " AND `Application`.`name` LIKE '".DEFAULT_APP_NAME."'";
$_conditions .= " AND `Application`.`version` LIKE '1.5'"; $_conditions .= " AND `Application`.`version` LIKE '".DEFAULT_APP_VERSION."'";
} }
} else { } else {
// I'm providing a default here, because otherwise all results will be // I'm providing a default here, because otherwise all results will be
// returned (across all applications) and that is not desired // returned (across all applications) and that is not desired
$_conditions .= " AND `Application`.`name` LIKE 'Mozilla Firefox'"; $_conditions .= " AND `Application`.`name` LIKE '".DEFAULT_APP_NAME."'";
$_conditions .= " AND `Application`.`version` LIKE '1.5'"; $_conditions .= " AND `Application`.`version` LIKE '".DEFAULT_APP_VERSION."'";
} }
// Save ourselves some joins
$this->Application->unBindModel(array('hasAndBelongsToMany' => array('Intention','Issue')));
$_application_id = $this->Application->findAll($_conditions, 'Application.id'); $_application_id = $this->Application->findAll($_conditions, 'Application.id');
// Next determine our collection
if (!empty($params['collection'])) {
$_collection_id = $this->Choice->Collection->findByDescription($params['collection']);
$clear = true;
foreach ($_collection_id['Application'] as $var => $val) {
if ($_application_id[0]['Application']['id'] == $val['id']) {
$clear = false;
}
}
if ($clear) {
$_id = $this->Application->getMaxCollectionId($_application_id[0]['Application']['id'], 'issue');
$_collection_id['Collection']['id'] = $_id[0][0]['max'];
}
} else {
$_id = $this->Application->getMaxCollectionId($_application_id[0]['Application']['id'], 'issue');
$_collection_id['Collection']['id'] = $_id[0][0]['max'];
}
// The second query will retrieve all the issues that are related to our // The second query will retrieve all the issues that are related to our
// application. // application.
$_query = " $_query = "
SELECT SELECT
issues.description, issues.id choices.description, choices.id
FROM FROM
applications_issues, issues choices
WHERE JOIN choices_collections ON choices_collections.choice_id = choices.id
applications_issues.application_id = {$_application_id[0]['Application']['id']} JOIN collections ON collections.id = choices_collections.collection_id
AND JOIN applications_collections ON applications_collections.collection_id = collections.id
issues.id = applications_issues.issue_id JOIN applications ON applications.id = applications_collections.application_id
AND applications.id = {$_application_id[0]['Application']['id']}
AND choices.type = 'issue'
";
if (!empty($_collection_id['Collection']['id'])) {
$_query .= "AND collections.id = {$_collection_id['Collection']['id']}";
}
$_query .= "
ORDER BY ORDER BY
issues.description DESC choices.description DESC
"; ";
$_issues = $this->query($_query); $_issues = $this->query($_query);
@ -363,24 +408,27 @@ class Result extends AppModel {
// Cake has a pretty specific way it stores data, and this is consistent // Cake has a pretty specific way it stores data, and this is consistent
// with the old query. Here we start our results array so it's holding the // with the old query. Here we start our results array so it's holding the
// descriptions and a zeroed total // descriptions and a zeroed total
$_results[$val['issues']['id']]['issues']['description'] = $val['issues']['description']; $_results[$val['choices']['id']]['choices']['description'] = $val['choices']['description'];
$_results[$val['issues']['id']][0]['total'] = 0; // default to nothing - this will get filled in later $_results[$val['choices']['id']][0]['total'] = 0; // default to nothing - this will get filled in later
// Since we're already walking through this loop, we might as well build // Since we're already walking through this loop, we might as well build
// up a query string to get our totals // up a query string to get our totals
$_issue_ids .= empty($_issue_ids) ? $val['issues']['id'] : ', '.$val['issues']['id']; $_issue_ids .= empty($_issue_ids) ? $val['choices']['id'] : ',
'.$val['choices']['id'];
} }
$_query = " $_query = "
SELECT SELECT
issues_results.issue_id, count(id) choices_results.choice_id, count(results.id)
AS AS
total total
FROM FROM
issues_results results
JOIN results ON results.id=issues_results.result_id JOIN choices_results ON results.id = choices_results.result_id
AND results.application_id={$_application_id[0]['Application']['id']} WHERE
AND issues_results.issue_id in ({$_issue_ids}) results.application_id = {$_application_id[0]['Application']['id']}
AND
choices_results.choice_id in ({$_issue_ids})
"; ";
if (!empty($params['start_date'])) { if (!empty($params['start_date'])) {
@ -401,13 +449,13 @@ class Result extends AppModel {
} }
} }
$_query .= "GROUP BY issue_id"; $_query .= " GROUP BY choices_results.choice_id";
$ret = $this->query($_query); $ret = $this->query($_query);
foreach ($ret as $var => $val) { foreach ($ret as $var => $val) {
// fill in the totals we retrieved // fill in the totals we retrieved
$_results[$val['issues_results']['issue_id']][0]['total'] = $val[0]['total']; $_results[$val['choices_results']['choice_id']][0]['total'] = $val[0]['total'];
} }
return $_results; return $_results;
@ -427,10 +475,10 @@ class Result extends AppModel {
// Apparently these are all escaped for us by cake. It still makes me // Apparently these are all escaped for us by cake. It still makes me
// nervous. // nervous.
$_application_id = $data['Application']['id']; $_application_id = $data['Application']['id'];
$_intention_id = $data['Result']['intention_id']; $_intention_id = $data['Intention']['id']; // Doesn't quite conform to cake standards
$_comments = $data['Result']['comments']; $_comments = $data['Result']['comments'];
$_issues_text = $data['issues_results']['other']; $_issues_text = $this->Sanitize->Sql($data['Issue']['text']);
$_intention_text = $data['Result']['intention_text']; $_intention_text = $this->Sanitize->Sql($data['Intention']['text']);
// Joined for legacy reasons // Joined for legacy reasons
$_user_agent = mysql_real_escape_string("{$data['ua'][0]} {$data['lang'][0]}"); $_user_agent = mysql_real_escape_string("{$data['ua'][0]} {$data['lang'][0]}");
$_http_user_agent = mysql_real_escape_string($_SERVER['HTTP_USER_AGENT']); $_http_user_agent = mysql_real_escape_string($_SERVER['HTTP_USER_AGENT']);
@ -443,16 +491,21 @@ class Result extends AppModel {
// Special cases for the "other" fields. If their corresponding option isn't // Special cases for the "other" fields. If their corresponding option isn't
// set, we don't want the field values. // set, we don't want the field values.
// issue is determined below // issue is determined below
$_issue_array = $this->Issue->findByDescription('other'); $this->Choice->unBindModel(array('hasAndBelongsToMany' => array('Result')));
$_intention_array = $this->Intention->findByDescription('other'); $this->Choice->unBindModel(array('hasAndBelongsToMany' => array('Collection')));
//$_issue_array = $this->Issue->findByDescription('other');
$_conditions = "description LIKE 'Other' AND type='intention'";
$_intention_array = $this->Choice->findAll($_conditions);
if ($_intention_id != $_intention_array['Intention']['id']) { $_conditions = "description LIKE 'Other' AND type='issue'";
$_issue_array = $this->Choice->findAll($_conditions);
if ($_intention_id != $_intention_array[0]['Choice']['id']) {
echo 'not equal!';
$_intention_text = ''; $_intention_text = '';
} }
$this->set('application_id', $_application_id); $this->set('application_id', $_application_id);
$this->set('intention_id', $_intention_id);
$this->set('intention_text', $_intention_text);
$this->set('comments', $_comments); $this->set('comments', $_comments);
$this->set('useragent', $_user_agent); $this->set('useragent', $_user_agent);
$this->set('http_user_agent', $_http_user_agent); $this->set('http_user_agent', $_http_user_agent);
@ -460,12 +513,29 @@ class Result extends AppModel {
// We kinda overrode $this's save(), so we'll have to ask our guardians // We kinda overrode $this's save(), so we'll have to ask our guardians
parent::save(); parent::save();
$_result_id = $this->getLastInsertID();
// The issues_results table isn't represented by a class in cake, so we have // Insert our intention
if (!empty($_intention_id)) {
$_query = "
INSERT INTO choices_results(
result_id,
choice_id,
other
) VALUES (
{$_result_id},
{$_intention_id},
'{$_intention_text}'
)
";
$this->query($_query);
}
// The choices_results table isn't represented by a class in cake, so we have
// to do the query manually. // to do the query manually.
if (!empty($data['Issue']['id'])) { if (!empty($data['Issue']['id'])) {
$_result_id = $this->getLastInsertID();
$_query = ''; $_query = '';
foreach ($data['Issue']['id'] as $var => $val) { foreach ($data['Issue']['id'] as $var => $val) {
@ -475,15 +545,15 @@ class Result extends AppModel {
} }
// If the 'other' id matches the id we're putting in, add the issue text // If the 'other' id matches the id we're putting in, add the issue text
$_other_text = ($val == $_issue_array['Issue']['id']) ? $_issues_text : ''; $_other_text = ($val == $_issue_array[0]['Choice']['id']) ? $_issues_text : '';
$_query .= empty($_query) ? "({$_result_id},{$val},'{$_other_text}')" : ",({$_result_id},{$val},'{$_other_text}')"; $_query .= empty($_query) ? "({$_result_id},{$val},'{$_other_text}')" : ",({$_result_id},{$val},'{$_other_text}')";
} }
$_query = " $_query = "
INSERT INTO issues_results( INSERT INTO choices_results(
result_id, result_id,
issue_id, choice_id,
other other
) VALUES ) VALUES
{$_query} {$_query}
@ -494,5 +564,6 @@ class Result extends AppModel {
return false; return false;
} }
} }
?> ?>

Просмотреть файл

@ -32,5 +32,25 @@ class ExportHelper
return "{$this->webroot}{$url}{$seperator}{$arguments}"; return "{$this->webroot}{$url}{$seperator}{$arguments}";
} }
function buildCsvExportString($params)
{
if (array_key_exists('product', $params)) {
$filename = $params['product'];
} else {
$filename = DEFAULT_APP_NAME.' '.DEFAULT_APP_VERSION;
}
// Our filenames have underscores
$filename = str_replace(' ','_',$filename);
$filename = "export-{$filename}.csv";
if (is_readable(ROOT.DS.APP_DIR.DS.WEBROOT_DIR.DS.'export'.DS.$filename)) {
return "{$this->webroot}export/{$filename}";
} else {
return '';
}
}
} }
?> ?>

Просмотреть файл

@ -1,12 +1,12 @@
<?php echo $html->formTag('/results/add'); ?> <?php echo $html->formTag('/results/add'); ?>
<?php if(!empty($intentions['Intention'])): ?> <?php if(!empty($intentions)): ?>
<h2>How did you intend to use <?php echo $intentions['Application']['name']; ?> when you installed it?</h2> <h2>How did you intend to use <?php echo $intentions[0]['applications']['name']; ?> when you installed it?</h2>
<?php foreach ($intentions['Intention'] as $var => $val) : ?> <?php foreach ($intentions as $var => $val) : ?>
<input type="radio" name="data[Result][intention_id]" id="radio<?php echo $val['id']; ?>" value="<?php echo $val['id']; ?>"><label for="radio<?php echo $val['id']; ?>"><?php echo $val['description']; ?></label> <input type="radio" name="data[Intention][id]" id="radio<?php echo $val['choices']['id']; ?>" value="<?php echo $val['choices']['id']; ?>"><label for="radio<?php echo $val['choices']['id']; ?>"><?php echo $val['choices']['description']; ?></label>
<?php <?php
if ($val['description'] == 'Other') { if ($val['choices']['description'] == 'Other') {
echo $html->input('Result/intention_text'); echo $html->input('Intention/text');
} }
?> ?>
@ -14,21 +14,20 @@
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
<?php if(!empty($issues)): ?>
<?php if(!empty($issues['Issue'])): ?> <h2>Why did you uninstall <?php echo $issues[0]['applications']['name']; ?>? (select all that apply)</h2>
<h2>Why did you uninstall <?php echo $intentions['Application']['name']; ?>? (select all that apply)</h2> <?php foreach ($issues as $var => $val) : ?>
<?php foreach ($issues['Issue'] as $var => $val) : ?> <input type="checkbox" name="data[Issue][id][]" id="checkbox<?php echo $val['choices']['id']; ?>" value="<?php echo $val['choices']['id']; ?>"><label for="checkbox<?php echo $val['choices']['id']; ?>"><?php echo $val['choices']['description']; ?></label>
<input type="checkbox" name="data[Issue][id][]" id="checkbox<?php echo $val['id']; ?>" value="<?php echo $val['id']; ?>"><label for="checkbox<?php echo $val['id']; ?>"><?php echo $val['description']; ?></label>
<?php <?php
if ($val['description'] == 'Other') { if ($val['choices']['description'] == 'Other') {
echo $html->input('issues_results/other'); echo $html->input('Issue/text');
} }
?> ?>
<br /> <br />
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
<h2>How can we improve <?php echo $intentions['Application']['name']; ?>?</h2> <h2>How can we improve <?php echo $intentions[0]['applications']['name']; ?>?</h2>
<p>Please share your ideas, suggestions or details about any issues below.</p> <p>Please share your ideas, suggestions or details about any issues below.</p>
<p><?php echo $html->textarea('Result/comments',array('id'=>'comments'));?></p> <p><?php echo $html->textarea('Result/comments',array('id'=>'comments'));?></p>
@ -37,6 +36,5 @@
<?php echo $html->hidden('ua', 'value="'.$url_params['ua'].'"') ?> <?php echo $html->hidden('ua', 'value="'.$url_params['ua'].'"') ?>
<?php echo $html->hidden('lang', 'value="'.$url_params['lang'].'"') ?> <?php echo $html->hidden('lang', 'value="'.$url_params['lang'].'"') ?>
<p><?php echo $html->submit('Save') ?></p> <p><?php echo $html->submit('Save') ?></p>
</form> </form>

Просмотреть файл

@ -1,11 +1,11 @@
<h1><?php echo Inflector::humanize($this->name); ?></h1> <h1><?php echo Inflector::humanize($this->name); ?></h1>
<div id="queryform"> <div id="queryform">
<?php echo $html->formTag('/results/','get'); ?> <?php echo $html->formTag('/results/','get');?>
<span> <span>
<label for="start_date">Start Date:</label> <label for="start_date">Start:</label>
<input type="text" name="start_date" id="start_date" value="<? echo isset($url_params['start_date']) ? $url_params['start_date'] : ''; ?>" /> <input type="text" name="start_date" id="start_date" value="<? echo isset($url_params['start_date']) ? $url_params['start_date'] : ''; ?>" />
<label for="end_date">End Date:</label> <label for="end_date">End:</label>
<input type="text" name="end_date" id="end_date" value="<? echo isset($url_params['end_date']) ? $url_params['end_date'] : ''; ?>" /> <input type="text" name="end_date" id="end_date" value="<? echo isset($url_params['end_date']) ? $url_params['end_date'] : ''; ?>" />
</span> </span>
@ -20,6 +20,24 @@
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<?php if (!empty($collections)) : ?>
<label for="collection">Questions:</label>
<select name="collection" id="collection">
<?php
foreach ($collections as $select) :
$collection = $select['collections']['description'];
// php's built in functions won't decode UTF-8 or numeric
// entities. Since we've only got 1, I'm putting in this to
// get this done quickly. :(
$url_params['collection'] = str_replace('&#45;','-',urldecode($url_params['collection']));
echo "{$url_params['collection']}<br />";
$selected = ($collection == trim($url_params['collection'])) ? ' selected="selected" ' : '';
?>
<option<?=$selected?>><?=$collection?></option>
<?php endforeach; ?>
</select>
<?php endif; ?>
<input type="submit" name="submit" id="submit" value="Go" /> <input type="submit" name="submit" id="submit" value="Go" />
</form> </form>
@ -43,7 +61,7 @@
} }
foreach ($descriptionAndTotalsData as $var => $val) { foreach ($descriptionAndTotalsData as $var => $val) {
// We're putting this in a js string, so escape the double quotes // We're putting this in a js string, so escape the double quotes
$_description = str_replace('"','\"',$val['issues']['description']); $_description = str_replace('"','\"',$val['choices']['description']);
$_percentage = intval(($val[0]['total'] / $_total)*100); $_percentage = intval(($val[0]['total'] / $_total)*100);
$_dataset .= "[{$_count}, {$val[0]['total']}, \"{$_description} (n={$val[0]['total']}, {$_percentage}%)\"], "; $_dataset .= "[{$_count}, {$val[0]['total']}, \"{$_description} (n={$val[0]['total']}, {$_percentage}%)\"], ";
@ -56,12 +74,12 @@
<table class="results" summary="Firefox Uninstallation results."> <table class="results" summary="Firefox Uninstallation results.">
<tr><th>Reason for uninstalling</th><th>Total</th></tr> <tr><th>Reason for uninstalling</th><th>Total</th></tr>
<?php foreach ($descriptionAndTotalsData as $var => $val) : ?> <?php foreach ($descriptionAndTotalsData as $var => $val) : ?>
<tr><td><?=$val['issues']['description']?></td><td><?=$val[0]['total']?></td></tr> <tr><td><?=$val['choices']['description']?></td><td><?=$val[0]['total']?></td></tr>
<?php endforeach; ?> <?php endforeach; ?>
</table> </table>
</canvas> </canvas>
</div> </div>
<script type="text/javascript"> <script type="text/javascript"><!--
var layout = new PlotKit.Layout("bar", {"barOrientation":"horizontal"}); var layout = new PlotKit.Layout("bar", {"barOrientation":"horizontal"});
layout.addDataset("results", <?=$_dataset?>); layout.addDataset("results", <?=$_dataset?>);
layout.evaluate(); layout.evaluate();
@ -78,11 +96,17 @@
}); });
plotter.render(); plotter.render();
</script> --></script>
<div id="exportbox"> <div id="exportbox">
<a href="<?php echo $export->buildUrlString('results/comments/',$url_params); ?>">View Any Associated Comments»</a><br /> <a href="<?php echo $export->buildUrlString('results/comments/',$url_params); ?>">View Any Associated Comments»</a><br />
<?php
$export_url = $export->buildCsvExportString($url_params);
if (!empty($export_url)):
?>
<a href="<?php echo $export_url; ?>">Download the Complete Dataset»</a><br />
<?php endif; ?>
</div> </div>
<?php else: ?> <?php else: ?>