railpage/railpagecore

View on GitHub
lib/Images/Competitions.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

/**
 * Photo competitions
 * @since Version 3.9.1
 * @package Railpage
 * @author Michael Greenhill
 */

namespace Railpage\Images;

use Railpage\Users\User;
use Railpage\Users\Factory as UserFactory;
use Railpage\Config\Base as Config;
use Railpage\AppCore;
use Railpage\Module;
use Railpage\ContentUtility;
use Exception;
use DateTime;
use DateInterval;
use DateTimeZone;
use Zend_Db_Expr;

/**
 * Competitions
 */

class Competitions extends AppCore {
    
    /**
     * Competition status: open
     * @since Version 3.9.1
     * @const int STATUS_OPEN
     */
    
    const STATUS_OPEN = 0;
    
    /**
     * Competition status: closed to entries
     * @since Version 3.9.1
     * @const int STATUS_CLOSED
     */
    
    const STATUS_CLOSED = 1;
    
    /**
     * Competition status: Future event (for UI purposes only)
     * @since Version 3.9.1
     * @const int STATUS_FUTURE
     */
    
    const STATUS_FUTURE = 99;
    
    /**
     * Photo submission: approved
     * @since Version 3.9.1
     * @const int PHOTO_APPROVED
     */
    
    const PHOTO_APPROVED = 1;
    
    /**
     * Photo submission: unapproved
     * @since Version 3.9.1
     * @const int PHOTO_UNAPPROVED
     */
    
    const PHOTO_UNAPPROVED = 0;
    
    /**
     * Photo submission: rejected
     * @since Version 3.9.1
     * @const int PHOTO_REJECTED
     */
    
    const PHOTO_REJECTED = 2;
    
    /**
     * Default number of maximum votes per user per competition
     * @since Version 3.9.1
     * @const int MAX_VOTES_PER_USER
     */
    
    const MAX_VOTES_PER_USER = 5;
    
    /**
     * Constructor
     * @since Version 3.9.1
     */
    
    public function __construct() {
        parent::__construct(); 
        $this->Module = new Module("images.competitions"); 
    }
    
    /**
     * Get the list of competitions, optionally filter by status
     * @since Version 3.9.1
     * @param int $status
     * @return array
     */
    
    public function getCompetitions($status = NULL) {
        $query = "SELECT id FROM image_competition";
        $where = array(); 
        
        if (!is_null($status)) {
            if ($status == self::STATUS_OPEN) {
                $query .= " WHERE status = ?";
                $where[] = $status;
            } elseif ($status == self::STATUS_CLOSED) {
                $query .= " WHERE status = ? AND voting_date_close < ? ORDER BY voting_date_close DESC";
                $where[] = $status;
                $where[] = (new DateTime)->format("Y-m-d H:i:s");
            } elseif ($status == self::STATUS_FUTURE) {
                $query .= " WHERE submissions_date_open > ? ORDER BY submissions_date_open ASC";
                $where[] = (new DateTime)->format("Y-m-d H:i:s");
            }
        }
        
        $comps = array(); 
        
        foreach ($this->db->fetchAll($query, $where) as $row) {
            $Competition = new Competition($row['id']);
            $comps[] = $Competition->getArray(); 
        }
        
        return $comps;
    }
    
    /**
     * Get an associative array of users by submitted photos, votes and competitions won
     * @since Version 3.9.1
     * @return array
     */
    
    public function getLeagueTable() {
        /**
         SELECT 
            s.user_id, u.username, c.title AS comp_title, c.id AS comp_id, c.slug AS comp_slug, 
            s.image_id, i.meta AS image_meta,
            (SELECT COUNT(v.id) AS votes FROM image_competition_votes AS v WHERE v.competition_id = c.id AND v.image_id = s.image_id) AS votes
            FROM image_competition_submissions AS s
            LEFT JOIN nuke_users AS u ON s.user_id = u.user_id
            LEFT JOIN image_competition AS c ON c.id = s.competition_id
            LEFT JOIN image AS i ON s.image_id = i.id
            ORDER BY votes DESC
        */
        
        /*
        $query = "SELECT 
            u.user_id, u.username, 
            (SELECT COUNT(s.id) AS submissions FROM image_competition_submissions AS s WHERE s.user_id = u.user_id AND s.status = 1) AS submissions,
            (SELECT COUNT(v.id) AS votes FROM image_competition_votes AS v WHERE v.image_id = s.image_id AND s.user_id = u.user_id AND s.status = 1) AS votes,
            IFNULL((SELECT wins FROM (
                SELECT COUNT(wins.user_id) AS wins, wins.user_id FROM (
                    SELECT winners.user_id, winners.competition_id FROM (
                        SELECT COUNT(v.id) AS votes, sub.user_id, v.competition_id
                            FROM image_competition_votes AS v 
                            LEFT JOIN image_competition_submissions AS sub ON v.image_id = sub.image_id
                            LEFT JOIN image_competition AS c ON sub.competition_id = c.id 
                            WHERE c.status = 1 
                            GROUP BY v.competition_id, sub.user_id
                            ORDER BY v.competition_id, votes DESC
                        ) AS winners 
                        GROUP BY winners.competition_id
                    ) AS wins
                    GROUP BY wins.user_id
                ) AS wins
                WHERE user_id = s.user_id
            ), 0) AS wins
            FROM image_competition_submissions AS s
            LEFT JOIN nuke_users AS u ON s.user_id = u.user_id
            LEFT JOIN image_competition AS c ON c.id = s.competition_id
            GROUP BY user_id
            ORDER BY votes DESC, submissions DESC, username ASC";
            */
        
        $query = "SELECT played.user_id, u.username, COALESCE(played.num_played, 0) AS submissions, COALESCE(votes.num_votes, 0) AS votes, COALESCE(won.num_won, 0) AS wins
            FROM (
                SELECT s.user_id, COUNT(*) AS num_played 
                FROM image_competition_submissions AS s 
                LEFT JOIN image_competition AS c ON c.id = s.competition_id 
                WHERE s.status = 1 
                AND c.status = 1 
                GROUP BY s.user_id
            ) AS played
            LEFT JOIN (
                SELECT v.user_id, COUNT(*) AS num_votes 
                FROM image_competition_votes AS v
                LEFT JOIN image_competition AS c ON c.id = v.competition_id
                WHERE c.status = 1
                GROUP BY user_id
            ) AS votes ON played.user_id = votes.user_id
            LEFT JOIN (
                SELECT user_id, COUNT(*) AS num_won 
                FROM image_competition_submissions 
                WHERE winner = 1 
                GROUP BY user_id
            ) AS won ON won.user_id = played.user_id
            LEFT JOIN nuke_users AS u ON played.user_id = u.user_id
            ORDER BY num_won DESC, num_votes DESC, username ASC";
        
        $return = array(); 
        
        foreach ($this->db->fetchAll($query) as $row) {
            if (!($row['submissions'] == 0 && $row['votes'] == 0)) {
                $return[] = $row;
            }
        }
        
        return $return;
    }
    
    /**
     * Get competition theme suggestions
     * @since Version 3.9.1
     * @return array
     */
    
    public function getSuggestedThemes() {
        $Config = new Config;
        
        $themes = $Config->get("image.competition.suggestedthemes");
        
        return $themes === false ? array() : json_decode($themes, true); 
    }
    
    /**
     * Suggest a theme to add
     * @since Version 3.9.1
     * @return \Railpage\Images\Competitions
     * @param string $theme The short descriptive text for the theme (eg "At night", "Close up", etc)
     * @param boolean $winner True/false flag indicating if this theme has been suggested by a competition winner
     */
    
    public function suggestTheme($theme, $winner = null) {
        if (!$this->Author instanceof User) {
            throw new Exception("You have not set the author of this theme (hint: Competitions::setAuthor()");
        }
        
        if (empty($theme)) {
            throw new Exception("You haven't entered any text...");
        }
        
        $theme = ContentUtility::FormatTitle($theme);
        
        $themes = $this->getSuggestedThemes(); 
        
        array_unshift($themes, [
            "user" => [
                "id" => $this->Author->id,
                "username" => $this->Author->username
            ],
            "theme" => $theme,
            "winner" => $winner
        ]);
        
        $Config = new Config;
        $Config->set("image.competition.suggestedthemes", json_encode($themes), "Photo competition themes"); 
        
        return $this;
    }
    
    /**
     * Auto generate a competition for the next calendar month
     * @since Version 3.9.1
     * @return \Railpage\Images\Competition
     */
    
    public function autoPopulateNextComp() {
        $month = new DateTime("first day of next month"); 
        
        $title = $month->format("F Y");
        
        $Competition = new Competition($title);
        
        while (filter_var($Competition->id, FILTER_VALIDATE_INT)) {
            $month->add(new DateInterval("P1M"));
            $title = $month->format("F Y");
            $Competition = new Competition($title);
        }
        
        /**
         * If the comp ID isn't a valid int assume we need to create a new comp
         */
        
        if (!filter_var($Competition->id, FILTER_VALIDATE_INT)) {
            $Competition->title = $title;
            $Competition->SubmissionsDateOpen = clone $month;
            $Competition->SubmissionsDateClose = clone $month;
            $Competition->SubmissionsDateClose->add(new DateInterval("P14D"));
            $Competition->VotingDateOpen = clone $month;
            $Competition->VotingDateOpen->add(new DateInterval("P15D"));
            $Competition->VotingDateClose = clone $month;
            $Competition->VotingDateClose->add(new DateInterval("P1M"))->sub(new DateInterval("P1D"));
            $Competition->meta = array(
                "maxvotes" => self::MAX_VOTES_PER_USER
            );
            
            /**
             * Get a suggested theme
             */
            
            $themes = $this->getSuggestedThemes();
            $themes = array_reverse($themes, true);
            
            foreach ($themes as $theme) {
                if (!isset($theme['used']) || $theme['used'] == false) {
                    if (function_exists("format_topictitle")) {
                        $theme['theme'] = format_topictitle($theme['theme']);
                    }
                    
                    $Competition->theme = $theme['theme']; #sprintf("%s (suggested by %s)", $theme['theme'], $theme['user']['username']);
                    
                    if ($theme['winner'] == true) {
                        break;
                    }
                }
            }
        }
        
        return $Competition;
    }
    
    /**
     * Get contestants in previous competitions
     * @since Version 3.9.1
     * @return array
     */
    
    public function getPreviousContestants() {
        
        $query = "SELECT s.user_id, u.username, u.user_email AS contact_email, concat('/user/', s.user_id) AS url 
            FROM image_competition_submissions AS s 
                LEFT JOIN nuke_users AS u ON s.user_id = u.user_id 
            GROUP BY user_id";
        
        return $this->db->fetchAll($query);

    }
    
    /**
     * Get the next photo competition
     * @since Version 3.10.0
     * @param \Railpage\Gallery\Competition $photoComp
     * @return \Railpage\Gallery\Competition
     */
    
    public function getNextCompetition(Competition $photoComp) {
        
        $query = "SELECT id 
            FROM image_competition
            WHERE submissions_date_close >= NOW()
            AND id != ?
            ORDER BY submissions_date_open ASC
            LIMIT 0, 1";
        
        $params = [
            $photoComp->id,
        ];
        
        $id = $this->db->fetchOne($query, $params); 
        
        try {
            $photoComp = new Competition($id); 
        } catch (Exception $e) {
            // throw it away
        }
        
        if ($photoComp instanceof Competition && filter_var($photoComp->id, FILTER_VALIDATE_INT)) {
            return $photoComp; 
        } 
        
        $NextComp = $this->autoPopulateNextComp(); 
        $NextComp->setAuthor(UserFactory::CreateUser(User::SYSTEM_USER_ID))->commit(); 
        
        return $NextComp;
        
    }
    
    /**
     * Get a list of image screeners
     * @since Version 3.10.0
     * @return array
     */
    
    public static function getScreeners() {
        
        
        
    }
    
}