PageRenderTime 27ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/models/election.php

https://bitbucket.org/sahkoinenaanestys/sahkoinenaanestys
PHP | 422 lines | 310 code | 52 blank | 60 comment | 102 complexity | 8c6b27bd1f5eccf0b5af7cb24fc66a7e MD5 | raw file
  1. <?php
  2. /**
  3. * Model for election database table.
  4. *
  5. * Copyright (c) 2013 Janne Seppänen, Antti Ranta, Matias Ylipelto
  6. * This program is made available under the terms of the MIT License.
  7. */
  8. class Election_Model extends Model
  9. {
  10. protected $idColumn = 'electionId';
  11. protected $tableName = 'election';
  12. //States
  13. const STATE_CLOSED = 0;
  14. const STATE_ACTIVE = 1;
  15. const STATE_ENDED = 2;
  16. const STATE_HISTORY = 3;
  17. //Result calculation options
  18. const CALCULATE_COALITIONS = 1;
  19. const NO_COALITIONS = 0;
  20. const ONLY_ONE_OPEN_ELECTION_ALLOWED = -2;
  21. const TOTAL_SEATS = 74; //37*2
  22. /**
  23. * Fetches the current ongoing election
  24. *
  25. * Return type can be chosen:
  26. * 'array' = returns all the database information about the election as an associative array
  27. * 'id' = returns the election id field value
  28. *
  29. * @param string $returnType
  30. * @return array|int|boolean
  31. */
  32. public function fetchCurrentElection($returnType = 'array')
  33. {
  34. $sql = "SELECT electionId, name, status, startDate, endDate, calcMethod\n".
  35. "FROM {$this->tableName}\n".
  36. "WHERE status='".self::STATE_ACTIVE."' AND startDate < NOW() AND endDate > NOW()\n".
  37. "LIMIT 1";
  38. if(!in_array($returnType, array('id', 'array')))
  39. die("Return type '{$returnType}' was not valid.");
  40. switch ($returnType) {
  41. case 'array':
  42. $electionArr = $this->fetchQueryResults($sql, array('singleRow' => true));
  43. if(!$electionArr)
  44. return false;
  45. return $electionArr;
  46. break;
  47. case 'id':
  48. $electionId = $this->fetchQueryResults($sql, array('fetchColumn' => 0));
  49. if(!$electionId)
  50. return false;
  51. return $electionId;
  52. break;
  53. }
  54. }
  55. /**
  56. * Fetches the current closed, open and ended elections
  57. *
  58. * @return array|int|boolean
  59. */
  60. public function fetchActiveElections()
  61. {
  62. $sql = "SELECT electionId, name, status, startDate, endDate, calcMethod\n".
  63. "FROM {$this->tableName}\n".
  64. "WHERE (status=? OR status=? OR status=?)";
  65. $paramArr = array(Election_Model::STATE_ACTIVE, Election_Model::STATE_CLOSED, Election_Model::STATE_ENDED);
  66. $result = $this->fetchQueryResults($sql,null,$paramArr);
  67. if(!$result)
  68. return false;
  69. return $result;
  70. }
  71. public function fetchPastElections()
  72. {
  73. $sql = "SELECT electionId, name, status, startDate, endDate,\n".
  74. "calcMethod, totalWwwVotes, totalPaperVotes, \n".
  75. "(SELECT SUM(votes + paperVotes) FROM candidate WHERE candidateNum=? AND candidate.election=election.electionId) ".
  76. "AS emptyVotes\n". //,
  77. //"IF((totalWwwVotes + totalPaperVotes) > 0, (totalWwwVotes + totalPaperVotes) / ".
  78. //"(SELECT COUNT(*) FROM voter WHERE canVote=? AND hasVoted > 0 AND election=electionId), 0) ".
  79. //"AS voterTurnout \n".
  80. "FROM {$this->tableName} WHERE status=?";
  81. $paramArr = array(Candidate_Model::EMPTY_VOTE,/* Voter_Model::HAS_VOTING_RIGHT,*/ Election_Model::STATE_HISTORY);
  82. $result = $this->fetchQueryResults($sql,null,$paramArr);
  83. if(!$result)
  84. return false;
  85. return $result;
  86. }
  87. /**
  88. * This method handles updating and inserting election data.
  89. *
  90. * This method cannot be used to modify these fields: name, status, startDate, startTime, endDate, endTime
  91. *
  92. * @param array $electionArr
  93. * @param int $electionId
  94. * @return int
  95. */
  96. public function updateElection($electionArr, $electionId = 0)
  97. {
  98. $electionId = (int)$electionId;
  99. $startDate = date('Y-m-d', strtotime($electionArr['startDate'])).' ';
  100. $startDate .= date('H:i:s', strtotime($electionArr['startTime']));
  101. $endDate = date('Y-m-d', strtotime($electionArr['endDate'])).' ';
  102. $endDate .= date('H:i:s', strtotime($electionArr['endTime']));
  103. $name = htmlspecialchars($electionArr['name'], ENT_QUOTES, "UTF-8");
  104. $paramArr = array('name'=>$name, 'startDate'=>$startDate,'endDate'=>$endDate, 'status'=>(int)$electionArr['status'], 'calcMethod'=>(int)$electionArr['calcMethod']);
  105. $sql = "name, startDate, endDate, status, calcMethod";
  106. $this->beginTransaction();
  107. //check that there are no other open elections than this
  108. if($electionArr['status'] == self::STATE_ACTIVE){
  109. $activeElections = $this->fetchByField('status', Election_Model::STATE_ACTIVE);
  110. if($activeElections != false && count($activeElections) > 0){
  111. $found = false;
  112. foreach($activeElections as $activeElection){
  113. if($activeElection['electionId'] == $electionId){
  114. $found = true;
  115. break;
  116. }
  117. }
  118. if(!$found){
  119. $this->rollBack();
  120. return self::ONLY_ONE_OPEN_ELECTION_ALLOWED;
  121. }
  122. }//if2
  123. }
  124. if($electionId == 0) { //Insert a new record
  125. if($this->insertInto($sql, $paramArr) == false) {
  126. $this->rollBack();
  127. return parent::FAILURE;
  128. }
  129. $id = $this->lastInsertId();
  130. //insert empty vote
  131. $candidateModel = new Candidate_Model;
  132. if($candidateModel->fetchByCompositeId(Candidate_Model::EMPTY_VOTE, $id) == false){
  133. $params = array('candidateNum'=>Candidate_Model::EMPTY_VOTE, 'firstName'=>EMPTY_VOTE_TEXT, 'election'=>$id);
  134. $query = "candidateNum, firstName, election";
  135. if($candidateModel->insertInto($query, $params) == false){
  136. $this->rollBack();
  137. return parent::FAILURE;
  138. }
  139. }
  140. }else { //Update existing record
  141. $paramArr[':electionId'] = $electionId;
  142. $sql .= "\nWHERE electionId = :electionId";
  143. if($this->updateSet($sql, $paramArr) == false){
  144. $this->rollBack();
  145. return parent::FAILURE;
  146. }
  147. }
  148. $this->commit();
  149. return parent::SUCCESS;
  150. }
  151. public function removeElection($electionId)
  152. {
  153. $this->beginTransaction();
  154. if(!is_null($electionId) && $electionId != 0) {
  155. $candidateModel = new Candidate_Model;
  156. if($candidateModel->countByFieldValues(array('election'=>$electionId)) > 0){
  157. if($candidateModel->removeByField("election", $electionId) == false) {
  158. $candidateModel->rollBack();
  159. return false;
  160. }
  161. }
  162. $voterModel = new Voter_Model;
  163. if($voterModel->countByFieldValues(array('election'=>$electionId)) > 0){
  164. if($voterModel->removeByField("election", $electionId) == false) {
  165. $voterModel->rollBack();
  166. return false;
  167. }
  168. }
  169. $allianceModel = new Alliance_Model;
  170. if($allianceModel->countByFieldValues(array('election'=>$electionId)) > 0){
  171. if($allianceModel->removeByField("election", $electionId) == false) {
  172. $allianceModel->rollBack();
  173. return false;
  174. }
  175. }
  176. $coalitionModel = new Coalition_Model;
  177. if($coalitionModel->countByFieldValues(array('election'=>$electionId)) > 0){
  178. if($coalitionModel->removeByField("election", $electionId) == false) {
  179. $coalitionModel->rollBack();
  180. return false;
  181. }
  182. }
  183. if($this->removeByField("electionId", $electionId) == false) {
  184. $this->rollBack();
  185. return false;
  186. }
  187. }
  188. $this->commit();
  189. return true;
  190. }
  191. public function endElection($electionId)
  192. {
  193. if(is_null($electionId) || $electionId==0)
  194. return false;
  195. $paramArr = array('status' => self::STATE_ENDED);
  196. $sql = "status";
  197. $paramArr[':electionId'] = $electionId;
  198. $sql .= "\nWHERE electionId = :electionId";
  199. if($this->updateSet($sql, $paramArr) == false) {
  200. $this->rollBack();
  201. return false;
  202. }
  203. return true;
  204. }
  205. public function startOrStopElectionElection($electionId, $curStatus)
  206. {
  207. if(is_null($electionId) || $electionId==0)
  208. return false;
  209. if($curStatus == self::STATE_ACTIVE){
  210. $paramArr = array('status' => self::STATE_CLOSED);
  211. }else if($curStatus == self::STATE_CLOSED){
  212. $paramArr = array('status' => self::STATE_ACTIVE);
  213. }
  214. $sql = "status";
  215. $paramArr[':electionId'] = $electionId;
  216. $sql .= "\nWHERE electionId = :electionId";
  217. if($this->updateSet($sql, $paramArr) == false) {
  218. $this->rollBack();
  219. return false;
  220. }
  221. return true;
  222. }
  223. public function putElectionToHistory($electionId)
  224. {
  225. if(is_null($electionId) || $electionId==0)
  226. return false;
  227. //calculate total papervotes and wwwvoter
  228. $voterModel = new Voter_Model;
  229. $wwwVotes = $voterModel->countVotes($electionId, Voter_Model::VOTE_METHOD_WWW);
  230. $paperVotes = $voterModel->countVotes($electionId, Voter_Model::VOTE_METHOD_PAPER);
  231. //update election status and votes
  232. $paramArr = array('status'=>self::STATE_HISTORY,'totalWwwVotes'=>$wwwVotes,
  233. 'totalPaperVotes'=>$paperVotes);
  234. $sql = "status,totalWwwVotes,totalPaperVotes";
  235. $paramArr[':electionId'] = $electionId;
  236. $sql .= "\nWHERE electionId = :electionId";
  237. if($this->updateSet($sql, $paramArr) == false) {
  238. $this->rollBack();
  239. return false;
  240. }
  241. //remove election voters
  242. if($voterModel->removeByField('election', $electionId) == false){
  243. $voterModel->rollBack();
  244. return false;
  245. }
  246. return true;
  247. }
  248. public function setResultsCalculated($electionId, $value)
  249. {
  250. if(is_null($electionId) || $electionId==0)
  251. return false;
  252. $paramArr = array('resultsCalculated'=>$value);
  253. $sql = "resultsCalculated";
  254. $paramArr[':electionId'] = $electionId;
  255. $sql .= "\nWHERE electionId = :electionId";
  256. if($this->updateSet($sql, $paramArr) == false) {
  257. $this->rollBack();
  258. return false;
  259. }
  260. return true;
  261. }
  262. /**
  263. * This function tells whether or not information regarding particular election can be deleted.
  264. *
  265. * Returns true if election with given id is in closed status.
  266. * Second parameter tells wether or not the time frame consisting of the
  267. * starting and ending dates should be checked.
  268. *
  269. * @param int $electionId
  270. * @param bool $compareDates
  271. * @return boolean
  272. */
  273. public function canBeDeleted($electionId, $compareDates = false)
  274. {
  275. $election = $this->fetchById((int) $electionId);
  276. if(empty($election))
  277. return false;
  278. //Status has to be closed
  279. if($election['status'] != self::STATE_CLOSED)
  280. return false;
  281. if ($compareDates) {
  282. //Current time cannot match the time frame of the election
  283. $now = date("Y-m-d H:i:s");
  284. if($election['startDate'] < $now && $election['endDate'] > $now)
  285. return false;
  286. }
  287. return true;
  288. }
  289. /**
  290. * This method validates the edit form's $_POST parameters and returns the valid input fields and error messages in an array.
  291. *
  292. * @param array $postArr
  293. * @return array
  294. */
  295. public function validateEditForm($postArr)
  296. {
  297. $electionArr = array();
  298. $errors = array();
  299. $formFields = array('name', 'startDate', 'startTime', 'endDate', 'endTime', 'calcMethod', 'status');
  300. $mandatory = array('name', 'startDate', 'startTime', 'endDate', 'endTime');
  301. $numeric = array('status', 'calcMethod');
  302. $datevalues = array('startDate', 'endDate');
  303. $timevalues = array('startTime', 'endTime');
  304. $invalidEndDate = false;
  305. $selectedStatus = $postArr['status'];
  306. if ($selectedStatus == 'stateOpen') {
  307. $postArr['status'] = self::STATE_ACTIVE;
  308. }else if ($selectedStatus == 'stateClosed') {
  309. $postArr['status'] = self::STATE_CLOSED;
  310. }else{
  311. $postArr['status'] = self::STATE_CLOSED;
  312. }
  313. $selectedCalcMethod = $postArr['calcMethod'];
  314. if ($selectedCalcMethod == 'countCoalitions') {
  315. $postArr['calcMethod'] = self::CALCULATE_COALITIONS;
  316. }else if ($selectedCalcMethod == 'noCoalitions') {
  317. $postArr['calcMethod'] = self::NO_COALITIONS;
  318. }else{
  319. $postArr['calcMethod'] = self::CALCULATE_COALITIONS;
  320. }
  321. $endDate = date('Y-m-d', strtotime($postArr['endDate'])).' '.date('H:i:s', strtotime($postArr['endTime']));
  322. $startDate = date('Y-m-d', strtotime($postArr['startDate'])).' '.date('H:i:s', strtotime($postArr['startTime']));
  323. if (strtotime($endDate) <= strtotime($startDate))
  324. $invalidEndDate = true;
  325. include_once SERVER_ROOT . '/lib/utils/data_validator.php';
  326. //Loop through form input and check that every mandatory field was filled
  327. foreach ($formFields as $field) {
  328. if(in_array($field, $mandatory) && empty($postArr[$field])) {
  329. $errors[$field] = MANDATORY_TEXT;
  330. }
  331. else if(in_array($field, $numeric) && !is_numeric($postArr[$field])) {
  332. $errors[$field] = HAS_TO_BE_A_NUMBER_TEXT;
  333. }
  334. else if(in_array($field, $datevalues) && !DataValidator::isValidDate($postArr[$field]) && !empty($postArr[$field])) {
  335. $errors[$field] = INVALID_DATEVALUE_TEXT;
  336. }
  337. else if(in_array($field, $timevalues) && !DataValidator::isValidTime($postArr[$field]) && !empty($postArr[$field])) {
  338. $errors[$field] = INVALID_TIMEVALUE_TEXT;
  339. }
  340. else if((in_array($field, $datevalues) || in_array($field, $timevalues)) && $invalidEndDate == true) {
  341. $errors[$field] = END_DATE_INVALID;
  342. }
  343. if(!isset($errors[$field]))
  344. $electionArr[$field] = $postArr[$field];
  345. }
  346. return array($electionArr, $errors);
  347. }
  348. public static function getElectionStateString($state)
  349. {
  350. switch ($state) {
  351. case self::STATE_CLOSED:
  352. return ELECTION_STATUS_CLOSED_TEXT;
  353. case self::STATE_ACTIVE:
  354. return ELECTION_STATUS_ACTIVE_TEXT;
  355. case self::STATE_ENDED:
  356. return ELECTION_STATUS_ENDED_TEXT;
  357. }
  358. }
  359. public static function checkActiveElectionDates($startDate = null, $endDate = null)
  360. {
  361. $returnVal = "";
  362. if(isset($startDate) && strtotime($startDate) > strtotime(date('d.m.Y H:i:s'))){
  363. $returnVal = ' (' . mb_strtolower(ELECTION_HAS_NOT_STARTED_YET_TEXT,'UTF-8') . ')';
  364. }
  365. if(isset($endDate) && strtotime($endDate) < strtotime(date('d.m.Y H:i:s'))){
  366. $returnVal = ' (' . mb_strtolower(ELECTION_STATUS_ENDED_TEXT,'UTF-8') . ')';
  367. }
  368. return $returnVal;
  369. }
  370. }