<?php
/***************************************************************************
- * Copyright (C) 2003-2007 Polytechnique.org *
+ * Copyright (C) 2003-2008 Polytechnique.org *
* http://opensource.polytechnique.org/ *
* *
* This program is free software; you can redistribute it and/or modify *
const MODE_ALL = 0;
const MODE_XANON = 1;
const MODE_XIDENT = 2;
- private static $longModes = array(self::MODE_ALL => "sondage ouvert à tout le monde, anonyme",
+ private static $longModes = array(self::MODE_ALL => "sondage ouvert à tout le monde, anonyme",
self::MODE_XANON => "sondage restreint aux polytechniciens, anonyme",
self::MODE_XIDENT => "sondage restreint aux polytechniciens, non anonyme");
private static $shortModes = array(self::MODE_ALL => "tout le monde, anonyme",
return ($long)? self::$longModes : self::$shortModes;
}
- private static $types = array('text' => 'texte court',
- 'textarea' => 'texte long',
- 'num' => 'numérique',
- 'radio' => 'radio',
- 'checkbox' => 'checkbox',
- 'personal' => 'informations personnelles');
+ private static $types = array('text' => 'Texte court',
+ 'textarea' => 'Texte long',
+ 'num' => 'Numérique',
+ 'radio' => 'Choix multiples (une réponse)',
+ 'checkbox' => 'Choix multiples (plusieurs réponses)',
+ 'radiotable' => 'Questions multiples à choix multiples (une réponse)',
+ 'checkboxtable' => 'Questions multiples à choix mutliples (plusieurs réponses)');
public static function getTypes()
{
{
$this->title = $args['title'];
$this->description = $args['description'];
- if (preg_match('#^\d{2}/\d{2}/\d{4}$#', $args['end'])) {
- $this->end = preg_replace('#^(\d{2})/(\d{2})/(\d{4})$#', '\3-\2-\1', $args['end']);
- } else {
- $this->end = (preg_match('#^\d{4}-\d{2}-\d{2}$#', $args['end']))? $args['end'] : '#';
- }
- $this->mode = $args['mode'];
- if ($args['mode'] == 0) {
+ $this->end = $args['end'];
+ $this->mode = (isset($args['mode']))? $args['mode'] : self::MODE_ALL;
+ if ($this->mode == self::MODE_ALL) {
$args['promos'] = '';
}
$this->promos = ($args['promos'] == '' || preg_match('#^(\d{4}-?|(\d{4})?-\d{4})(,(\d{4}-?|(\d{4})?-\d{4}))*$#', $args['promos']))? $args['promos'] : '#';
public function checkPromo($promo)
{
- $promos = explode('|', $this->promos);
+ if ($this->promos == '') {
+ return true;
+ }
+ $promos = explode(',', $this->promos);
foreach ($promos as $p) {
if ((preg_match('#^\d{4}$#', $p) && $p == $promo) ||
(preg_match('#^\d{4}-$#', $p) && intval(substr($p, 0, 4)) <= $promo) ||
$q = $this->questions[$k]->toArray();
$q['id'] = $k;
if ($this->isEnded()) { // if the survey is ended, then adds here the results of this question
- $sql = $this->questions[$k]->buildResultRequest();
- if ($sql != '') {
- $q['result'] = XDB::iterator($sql, $this->id, $k);
- }
+ $q['result'] = $this->questions[$k]->getResultArray($this->id, $k);
}
$qArr[$k] = $q;
}
}
// }}}
+ // {{{ function toCSV() : builds a CSV file containing all the results of the survey
+ public function toCSV($sep = ',', $enc = '"', $asep='|')
+ {
+ $nbq = count($this->questions);
+ //require_once dirname(__FILE__) . '/../../classes/varstream.php';
+ VarStream::init();
+ global $csv_output;
+ $csv_output = '';
+ $csv = fopen('var://csv_output', 'w');
+ $line = ($this->isMode(self::MODE_XIDENT))? array('id', 'Nom', 'Prenom', 'Promo') : array('id');
+ $qids = array();
+ for ($qid = 0; $qid < $nbq; $qid++) {
+ $qids[$qid] = count($line); // stores the first id of a question (in case of questions with subquestions)
+ array_splice($line, count($line), 0, $this->questions[$qid]->getCSVColumns()); // the first line contains the questions
+ }
+ $nbf = count($line);
+ $users = array();
+ if ($this->isMode(self::MODE_XIDENT)) { // if the mode is non anonymous
+ $sql = 'SELECT v.id AS vid, a.nom, a.prenom, a.promo
+ FROM survey_votes AS v
+ INNER JOIN auth_user_md5 AS a
+ ON a.user_id=v.user_id
+ WHERE v.survey_id={?}
+ ORDER BY vid ASC;';
+ $res = XDB::iterator($sql, $this->id); // retrieves all users data
+ for ($u = $res->next(); $u != null; $u = $res->next()) {
+ $users[$u['vid']] = array('nom' => $u['nom'], 'prenom' => $u['prenom'], 'promo' => $u['promo']);
+ }
+ }
+ $sql = 'SELECT v.id AS vid, a.question_id AS qid, a.answer AS answer
+ FROM survey_votes AS v
+ LEFT JOIN survey_answers AS a
+ ON a.vote_id=v.id
+ WHERE v.survey_id={?}
+ ORDER BY vid ASC, qid ASC, answer ASC;';
+ $res = XDB::iterator($sql, $this->id); // retrieves all answers from database
+ $cur = $res->next();
+ $vid = -1;
+ $vid_ = 0;
+ while ($cur != null) {
+ if ($vid != $cur['vid']) { // if the vote id changes, then starts a new line
+ fputcsv($csv, $line, $sep, $enc); // stores the former line into $csv_output
+ $vid = $cur['vid'];
+ $line = array_fill(0, $nbf, ''); // creates an array full of empty string
+ $line[0] = $vid_; // the first field is a 'clean' vote id (not the one stored in database)
+ if ($this->isMode(self::MODE_XIDENT)) { // if the mode is non anonymous
+ if (array_key_exists($vid, $users) && is_array($users[$vid])) { // and if the user data can be found
+ $line[1] = $users[$vid]['nom']; // adds the user data (in the first fields of the line)
+ $line[2] = $users[$vid]['prenom'];
+ $line[3] = $users[$vid]['promo'];
+ }
+ }
+ $vid_++;
+ }
+ $ans = $this->questions[$cur['qid']]->formatAnswer($cur['answer']); // formats the current answer
+ if (!is_null($ans)) {
+ if (is_array($ans)) {
+ $fid = $qids[$cur['qid']] + $ans['id']; // computes the field id
+ $a = $ans['answer'];
+ } else {
+ $fid = $qids[$cur['qid']];
+ $a = $ans;
+ }
+ if ($line[$fid] != '') { // if this field already contains something
+ $line[$fid] .= $asep; // then adds a separator before adding the new answer
+ }
+ $line[$fid] .= $a; // adds the current answer to the correct field
+ }
+ $cur = $res->next(); // gets next answer
+ }
+ fputcsv($csv, $line, $sep, $enc); // stores the last line into $csv_output
+ return $csv_output;
+ }
+ // }}}
+
// {{{ function factory($type, $args) : builds a question according to the given type
public function factory($t, $args)
{
return new SurveyRadio($args);
case 'checkbox':
return new SurveyCheckbox($args);
- case 'personal':
- return new SurveyPersonal($args);
+ case 'radiotable':
+ return new SurveyRadioTable($args);
+ case 'checkboxtable':
+ return new SurveyCheckboxTable($args);
default:
return null;
}
// {{{ questions manipulation functions
public function addQuestion($i, $c)
{
+ $i = intval($i);
if ($this->valid || $i > count($this->questions)) {
return false;
} else {
public function delQuestion($i)
{
+ $i = intval($i);
if ($this->valid || !array_key_exists($i, $this->questions)) {
return false;
} else {
// {{{ function checkSyntax() : checks syntax of the questions (currently the root only) before storing the survey in database
private static $errorMessages = array(
- "dateformat" => "la date de fin de sondage est mal formattée : elle doit respecter la syntaxe dd/mm/aaaa",
- "datepassed" => "la date de fin de sondage est déjà dépassée : vous devez préciser une date future",
- "promoformat" => "les restrictions à certaines promotions sont mal formattées"
+ "datepassed" => "la date de fin de sondage est déjà dépassée : vous devez préciser une date future",
+ "promoformat" => "les restrictions à certaines promotions sont mal formattées"
);
public function checkSyntax()
{
$rArr = array();
- if (!preg_match('#^\d{4}-\d{2}-\d{2}$#', $this->end)) {
- $rArr[] = array('question' => 'root', 'error' => self::$errorMessages["dateformat"]);
- } else {
- // checks that the end date given is not already passed
- // (unless the survey has already been validated : an admin can have a validated survey expired)
- if (!$this->valid && $this->isEnded()) {
- $rArr[] = array('question' => 'root', 'error' => self::$errorMessages["datepassed"]);
- }
+ // checks that the end date given is not already passed
+ // (unless the survey has already been validated : an admin can have a validated survey expired)
+ if (!$this->valid && $this->isEnded()) {
+ $rArr[] = array('question' => 'root', 'error' => self::$errorMessages["datepassed"]);
}
if ($this->promos != '' && !preg_match('#^(\d{4}-?|(\d{4})?-\d{4})(,(\d{4}-?|(\d{4})?-\d{4}))*$#', $this->promos)) {
$rArr[] = array('question' => 'root', 'error' => self::$errorMessages["promoformat"]);
public static function retrieveList($type, $tpl = true)
{
switch ($type) {
- case 'w':
- case 'waiting' :
- $where = 'valid=0';
- break;
case 'c':
case 'current':
- $where = 'valid=1 AND end > NOW()';
+ $where = 'end > NOW()';
break;
case 'o':
case 'old':
- $where = 'valid=1 AND end <= NOW()';
+ $where = 'end <= NOW()';
break;
default:
return null;
}
$sql = 'SELECT id, title, end, mode
FROM survey_surveys
- WHERE '.$where.';';
+ WHERE '.$where.'
+ ORDER BY end DESC;';
if ($tpl) {
return XDB::iterator($sql);
} else {
// {{{ static function retrieveSurvey() : gets a survey in database (and unserialize the survey object structure)
public static function retrieveSurvey($sid)
{
- $sql = 'SELECT questions, title, description, end, mode, promos, valid
+ $sql = 'SELECT questions, title, description, end, mode, promos
FROM survey_surveys
WHERE id={?}';
$res = XDB::query($sql, $sid);
if (is_null($data) || !is_array($data)) {
return null;
}
- $survey = new Survey($data, $sid, (boolean) $data['valid'], unserialize($data['questions']));
+ $survey = new Survey($data, $sid, true, unserialize($data['questions']));
return $survey;
}
// }}}
// {{{ static function retrieveSurveyInfo() : gets information about a survey (title, description, end date, restrictions) but does not unserialize the survey object structure
public static function retrieveSurveyInfo($sid)
{
- $sql = 'SELECT title, description, end, mode, promos, valid
+ $sql = 'SELECT title, description, end, mode, promos
FROM survey_surveys
WHERE id={?}';
$res = XDB::query($sql, $sid);
}
// }}}
+ // {{{ static function retrieveSurveyReq() : gets a survey request to validate
+ public static function retrieveSurveyReq($id)
+ {
+ require_once 'validations.inc.php';
+ $surveyreq = Validate::get_request_by_id($id);
+ if ($surveyreq == null) {
+ return null;
+ }
+ $data = array('title' => $surveyreq->title,
+ 'description' => $surveyreq->description,
+ 'end' => $surveyreq->end,
+ 'mode' => $surveyreq->mode,
+ 'promos' => $surveyreq->promos);
+ $survey = new Survey($data, $id, false, $surveyreq->questions);
+ return $survey;
+ }
+ // }}}
+
// {{{ function proposeSurvey() : stores a proposition of survey in database (before validation)
public function proposeSurvey()
{
- $sql = 'INSERT INTO survey_surveys
- SET questions={?},
- title={?},
- description={?},
- author_id={?},
- end={?},
- mode={?},
- promos={?},
- valid=0;';
- return XDB::execute($sql, serialize($this->questions), $this->title, $this->description, S::v('uid'), $this->end, $this->mode, $this->promos);
+ require_once 'validations.inc.php';
+ $surveyreq = new SurveyReq($this->title, $this->description, $this->end, $this->mode, $this->promos, $this->questions, S::user());
+ return $surveyreq->submit();
}
// }}}
// {{{ function updateSurvey() : updates a survey in database (before validation)
public function updateSurvey()
{
- if ($this->id == -1) {
- return false;
+ if ($this->valid) {
+ $sql = 'UPDATE survey_surveys
+ SET questions={?},
+ title={?},
+ description={?},
+ end={?},
+ mode={?},
+ promos={?}
+ WHERE id={?};';
+ return XDB::execute($sql, serialize($this->questions), $this->title, $this->description, $this->end, $this->mode, $this->promos, $this->id);
+ } else {
+ require_once 'validations.inc.php';
+ $surveyreq = Validate::get_request_by_id($this->id);
+ if ($surveyreq == null) {
+ return false;
+ }
+ return $surveyreq->updateReq($this->title, $this->description, $this->end, $this->mode, $this->promos, $this->questions);
}
- $sql = 'UPDATE survey_surveys
- SET questions={?},
- title={?},
- description={?},
- end={?},
- mode={?},
- promos={?}
- WHERE id={?};';
- return XDB::execute($sql, serialize($this->questions), $this->title, $this->description, $this->end, $this->mode, $this->promos, $this->id);
- }
- // }}}
-
- // {{{ static function validateSurvey() : validates a survey
- public static function validateSurvey($sid)
- {
- $sql = 'UPDATE survey_surveys
- SET valid=1
- WHERE id={?};';
- return XDB::execute($sql, $sid);
}
// }}}
}
// }}}
- // {{{ function checkAnswer : returns a correctly formatted answer (or nothing empty string if error)
+ // {{{ function checkAnswer : returns a correct answer (or a null value if error)
public function checkAnswer($ans)
{
return null;
}
// }}}
- // {{{ function buildResultRequest() : statistics on the results of the survey
- public function buildResultRequest()
+ // {{{ functions regarding the results of a survey
+ abstract public function getResultArray($sid, $qid);
+
+ public function formatAnswer($ans)
{
- return '';
+ return $ans;
+ }
+
+ public function getCSVColumns()
+ {
+ return $this->question;
}
// }}}
}
// }}}
-// {{{ abstract class SurveySimple and its derived classes : "opended" questions
+// {{{ abstract class SurveySimple and its derived classes : "open" questions
// {{{ abstract class SurveySimple extends SurveyQuestion
abstract class SurveySimple extends SurveyQuestion
{
return array($ans);
}
- public function buildResultRequest()
+ public function getResultArray($sid, $qid)
{
$sql = 'SELECT answer
FROM survey_answers
AND question_id={?}
ORDER BY RAND()
LIMIT 5;';
- return $sql;
+ $res = XDB::query($sql, $sid, $qid);
+ return $res->fetchAllAssoc();
}
}
// }}}
public function update($args)
{
parent::update($args);
- $this->choices = explode('|', $args['options']);
+ $this->choices = array();
+ foreach ($args['choices'] as $val) {
+ if (trim($val) || trim($val) == '0') {
+ $this->choices[] = $val;
+ }
+ }
}
public function toArray()
{
$rArr = parent::toArray();
$rArr['choices'] = $this->choices;
- $rArr['options'] = implode('|', $this->choices);
return $rArr;
}
- public function buildResultRequest()
+ public function getResultArray($sid, $qid)
{
$sql = 'SELECT answer, COUNT(id) AS count
FROM survey_answers
WHERE vote_id IN (SELECT id FROM survey_votes WHERE survey_id={?})
AND question_id={?}
GROUP BY answer ASC';
- return $sql;
+ $res = XDB::query($sql, $sid, $qid);
+ return $res->fetchAllAssoc();
+ }
+
+ public function formatAnswer($ans)
+ {
+ if (array_key_exists($ans, $this->choices)) {
+ return $this->choices[$ans];
+ } else {
+ return null;
+ }
}
}
// }}}
{
public function checkAnswer($ans)
{
- return (array_key_exists($ans, $this->choices))? array($ans) : null;
+ $a = intval($ans);
+ return (array_key_exists($a, $this->choices))? array($a) : null;
}
protected function getQuestionType()
// }}}
// }}}
-// {{{ class SurveyPersonal extends SurveyQuestion : allows easy and verified access to user's personal data (promotion, name...)
-// actually this type of question should be suppressed (non anonymous surveys are possible with survey modes)
-// and anyway it is not finished (checkAnswer implementation) : currently it does not store anything when a user votes
-class SurveyPersonal extends SurveyQuestion
+// {{{ abstract class SurveyTable and its derived classes : table question, each column represents a choice, each line represents a question
+// {{{ abstract class SurveyTable extends SurveyList
+abstract class SurveyTable extends SurveyList
{
- private $perm;
+ protected $subquestions;
public function update($args)
{
- $args['question'] = "Informations personnelles";
parent::update($args);
- $this->perm['promo'] = isset($args['promo'])? 1 : 0;
- $this->perm['name'] = isset($args['name'])? 1 : 0;
+ $this->subquestions = array();
+ foreach ($args['subquestions'] as $val) {
+ if (trim($val) || trim($val) == '0') {
+ $this->subquestions[] = $val;
+ }
+ }
}
- public function checkAnswer($ans)
+ public function toArray()
{
- if (intval($ans) == 1) {
- // requete mysql qvb
- return null;
+ $rArr = parent::toArray();
+ $rArr['subquestions'] = $this->subquestions;
+ return $rArr;
+ }
+
+ public function getResultArray($sid, $qid)
+ {
+ $sql = 'SELECT answer, COUNT(id) AS count
+ FROM survey_answers
+ WHERE vote_id IN (SELECT id FROM survey_votes WHERE survey_id={?})
+ AND question_id={?}
+ GROUP BY answer ASC';
+ $res = XDB::iterator($sql, $sid, $qid);
+ $result = array();
+ for ($i = 0; $i < count($this->subquestions); $i++) {
+ $result[$i] = array_fill(0, count($this->choices), 0);
+ }
+ while ($r = $res->next()) {
+ list($i, $j) = explode(':', $r['answer']);
+ $result[$i][$j] = $r['count'];
+ }
+ return $result;
+ }
+
+ public function formatAnswer($ans)
+ {
+ list($q, $c) = explode(':', $ans);
+ if (array_key_exists($q, $this->subquestions) && array_key_exists($c, $this->choices)) {
+ return array('id' => $q, 'answer' => $this->choices[$c]);
} else {
return null;
}
}
+ public function getCSVColumns()
+ {
+ $q = parent::getCSVColumns();
+ if (empty($this->subquestions)) {
+ return $q;
+ }
+ $a = array();
+ for ($k = 0; $k < count($this->subquestions); $k++) {
+ $a[$k] = $q.' : '.$this->subquestions[$k];
+ }
+ return $a;
+ }
+}
+// }}}
+
+// {{{ class SurveyRadioTable extends SurveyTable : SurveyTable with radio type choices
+class SurveyRadioTable extends SurveyTable
+{
+ public function checkAnswer($ans)
+ {
+ $rep = array();
+ foreach ($ans as $k => $a) {
+ if (!array_key_exists($k, $this->subquestions)) {
+ continue;
+ }
+ $a = intval($a);
+ if (array_key_exists($a, $this->choices)) {
+ $rep[] = $k . ':' . $a;
+ }
+ }
+ return (count($rep) == 0)? null : $rep;
+ }
+
protected function getQuestionType()
{
- return "personal";
+ return "radiotable";
}
- public function toArray()
+}
+// }}}
+
+// {{{ class SurveyCheckboxTable extends SurveyTable : SurveyTable with checkbox type choices
+class SurveyCheckboxTable extends SurveyTable
+{
+ public function checkAnswer($ans)
{
- $a = parent::toArray();
- $a['promo'] = $this->perm['promo'];
- $a['name'] = $this->perm['name'];
- return $a;
+ $rep = array();
+ foreach ($ans as $k => $aa) {
+ if (!array_key_exists($k, $this->subquestions)) {
+ continue;
+ }
+ foreach ($aa as $a) {
+ $a = intval($a);
+ if (array_key_exists($a, $this->choices)) {
+ $rep[] = $k . ':' . $a;
+ }
+ }
+ }
+ return (count($rep) == 0)? null : $rep;
}
+
+ protected function getQuestionType()
+ {
+ return "checkboxtable";
+ }
+
}
// }}}
+// }}}
-// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+// vim:set et sw=4 sts=4 ts=4 foldmethod=marker enc=utf-8:
?>