<commit_before><?php

/**
 * @name      ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
 *
 * This software is a derived product, based on:
 *
 * Simple Machines Forum (SMF)
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
 *
 * @version 1.0 Alpha
 *
 * Provide a display for forum statistics
 *
 */

if (!defined('ELK'))
	die('No access...');

/**
 * Statistics Controller
 */
class Stats_Controller extends Action_Controller
{
	/**
	 * Entry point for this class.
	 *
	 * @see Action_Controller::action_index()
	 */
	public function action_index()
	{
		// Call the right method... wait, we only know how to do
		// one thing (and do it well! :P)
		$this->action_stats();
	}

	/**
	 * Display some useful/interesting board statistics.
	 *
	 * gets all the statistics in order and puts them in.
	 * uses the Stats template and language file. (and main sub template.)
	 * requires the view_stats permission.
	 * accessed from ?action=stats.
	 */
	public function action_stats()
	{
		global $txt, $scripturl, $modSettings, $context;

		$db = database();

		isAllowedTo('view_stats');

		// Page disabled - redirect them out
		if (empty($modSettings['trackStats']))
			fatal_lang_error('feature_disabled', true);

		if (!empty($_REQUEST['expand']))
		{
			$context['robot_no_index'] = true;

			$month = (int) substr($_REQUEST['expand'], 4);
			$year = (int) substr($_REQUEST['expand'], 0, 4);
			if ($year > 1900 && $year < 2200 && $month >= 1 && $month <= 12)
				$_SESSION['expanded_stats'][$year][] = $month;
		}
		elseif (!empty($_REQUEST['collapse']))
		{
			$context['robot_no_index'] = true;

			$month = (int) substr($_REQUEST['collapse'], 4);
			$year = (int) substr($_REQUEST['collapse'], 0, 4);
			if (!empty($_SESSION['expanded_stats'][$year]))
				$_SESSION['expanded_stats'][$year] = array_diff($_SESSION['expanded_stats'][$year], array($month));
		}

		// Handle the XMLHttpRequest.
		if (isset($_REQUEST['xml']))
		{
			// Collapsing stats only needs adjustments of the session variables.
			if (!empty($_REQUEST['collapse']))
				obExit(false);

			$context['sub_template'] = 'stats';
			getDailyStats('YEAR(date) = {int:year} AND MONTH(date) = {int:month}', array('year' => $year, 'month' => $month));
			$context['yearly'][$year]['months'][$month]['date'] = array(
				'month' => sprintf('%02d', $month),
				'year' => $year,
			);
			return;
		}

		loadLanguage('Stats');
		loadTemplate('Stats');

		// Just a lil' help from our friend :P
		require_once(SUBSDIR . '/Stats.subs.php');

		// Build the link tree......
		$context['linktree'][] = array(
			'url' => $scripturl . '?action=stats',
			'name' => $txt['stats_center']
		);
		$context['page_title'] = $context['forum_name'] . ' - ' . $txt['stats_center'];

		$context['show_member_list'] = allowedTo('view_mlist');

		// Get averages...
		$averages = getAverages();
		// This would be the amount of time the forum has been up... in days...
		$total_days_up = ceil((time() - strtotime($averages['date'])) / (60 * 60 * 24));

		$context['average_posts'] = comma_format(round($averages['posts'] / $total_days_up, 2));
		$context['average_topics'] = comma_format(round($averages['topics'] / $total_days_up, 2));
		$context['average_members'] = comma_format(round($averages['registers'] / $total_days_up, 2));
		$context['average_online'] = comma_format(round($averages['most_on'] / $total_days_up, 2));
		$context['average_hits'] = comma_format(round($averages['hits'] / $total_days_up, 2));

		$context['num_hits'] = comma_format($averages['hits'], 0);

		// How many users are online now.
		$context['users_online'] = onlineCount();

		// Statistics such as number of boards, categories, etc.
		$context['num_boards'] = numBoards();
		$context['num_categories'] = numCategories();

		// Format the numbers nicely.
		$context['users_online'] = comma_format($context['users_online']);
		$context['num_boards'] = comma_format($context['num_boards']);
		$context['num_categories'] = comma_format($context['num_categories']);

		$context['num_members'] = comma_format($modSettings['totalMembers']);
		$context['num_posts'] = comma_format($modSettings['totalMessages']);
		$context['num_topics'] = comma_format($modSettings['totalTopics']);
		$context['most_members_online'] = array(
			'number' => comma_format($modSettings['mostOnline']),
			'date' => relativeTime($modSettings['mostDate'])
		);
		$context['latest_member'] = &$context['common_stats']['latest_member'];

		// Male vs. female ratio - let's calculate this only every four minutes.
		if (($context['gender'] = cache_get_data('stats_gender', 240)) == null)
		{
			$context['gender'] = genderRatio();

			// Set these two zero if the didn't get set at all.
			if (empty($context['gender']['males']))
				$context['gender']['males'] = 0;
			if (empty($context['gender']['females']))
				$context['gender']['females'] = 0;

			// Try and come up with some "sensible" default states in case of a non-mixed board.
			if ($context['gender']['males'] == $context['gender']['females'])
				$context['gender']['ratio'] = '1:1';
			elseif ($context['gender']['males'] == 0)
				$context['gender']['ratio'] = '0:1';
			elseif ($context['gender']['females'] == 0)
				$context['gender']['ratio'] = '1:0';
			elseif ($context['gender']['males'] > $context['gender']['females'])
				$context['gender']['ratio'] = round($context['gender']['males'] / $context['gender']['females'], 1) . ':1';
			elseif ($context['gender']['females'] > $context['gender']['males'])
				$context['gender']['ratio'] = '1:' . round($context['gender']['females'] / $context['gender']['males'], 1);

			cache_put_data('stats_gender', $context['gender'], 240);
		}

		$date = strftime('%Y-%m-%d', forum_time(false));

		// Members online so far today.
		$context['online_today'] = mostOnline($date);
		$context['online_today'] = comma_format((int) $context['online_today']);

		// Poster top 10.
		$context['top_posters'] = topPosters();
	
		foreach ($context['top_posters'] as $i => $poster)
		{
			$context['top_posters'][$i]['post_percent'] = round(($poster['num_posts'] * 100) / $max_num_posts);
			$context['top_posters'][$i]['num_posts'] = comma_format($context['top_posters'][$i]['num_posts']);
		}

		// Board top 10.
		$context['top_boards'] = topBoards();

		foreach ($context['top_boards'] as $i => $board)
		{
			$context['top_boards'][$i]['post_percent'] = round(($board['num_posts'] * 100) / $max_num_posts);
			$context['top_boards'][$i]['num_posts'] = comma_format($context['top_boards'][$i]['num_posts']);
		}

		// Topic replies top 10.
		$context['top_topics_replies'] = topTopicReplies();
	
		foreach ($context['top_topics_replies'] as $i => $topic)
		{
			$context['top_topics_replies'][$i]['post_percent'] = round(($topic['num_replies'] * 100) / $max_num_replies);
			$context['top_topics_replies'][$i]['num_replies'] = comma_format($context['top_topics_replies'][$i]['num_replies']);
		}

		// Topic views top 10.
		$context['top_topics_views'] = topTopicViews();

		foreach ($context['top_topics_views'] as $i => $topic)
		{
			$context['top_topics_views'][$i]['post_percent'] = round(($topic['num_views'] * 100) / $max_num_views);
			$context['top_topics_views'][$i]['num_views'] = comma_format($context['top_topics_views'][$i]['num_views']);
		}

		// Try to cache this when possible, because it's a little unavoidably slow.
		if (($members = cache_get_data('stats_top_starters', 360)) == null)
		{
			$request = $db->query('', '
				SELECT id_member_started, COUNT(*) AS hits
				FROM {db_prefix}topics' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
				WHERE id_board != {int:recycle_board}' : '') . '
				GROUP BY id_member_started
				ORDER BY hits DESC
				LIMIT 20',
				array(
					'recycle_board' => $modSettings['recycle_board'],
				)
			);
			$members = array();
			while ($row = $db->fetch_assoc($request))
				$members[$row['id_member_started']] = $row['hits'];
			$db->free_result($request);

			cache_put_data('stats_top_starters', $members, 360);
		}

		if (empty($members))
			$members = array(0 => 0);

		// Topic poster top 10.
		$members_result = $db->query('top_topic_starters', '
			SELECT id_member, real_name
			FROM {db_prefix}members
			WHERE id_member IN ({array_int:member_list})
			ORDER BY FIND_IN_SET(id_member, {string:top_topic_posters})
			LIMIT 10',
			array(
				'member_list' => array_keys($members),
				'top_topic_posters' => implode(',', array_keys($members)),
			)
		);
		$context['top_starters'] = array();
		$max_num_topics = 1;
		while ($row_members = $db->fetch_assoc($members_result))
		{
			$context['top_starters'][] = array(
				'name' => $row_members['real_name'],
				'id' => $row_members['id_member'],
				'num_topics' => $members[$row_members['id_member']],
				'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'],
				'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_members['id_member'] . '">' . $row_members['real_name'] . '</a>'
			);

			if ($max_num_topics < $members[$row_members['id_member']])
				$max_num_topics = $members[$row_members['id_member']];
		}
		$db->free_result($members_result);

		foreach ($context['top_starters'] as $i => $topic)
		{
			$context['top_starters'][$i]['post_percent'] = round(($topic['num_topics'] * 100) / $max_num_topics);
			$context['top_starters'][$i]['num_topics'] = comma_format($context['top_starters'][$i]['num_topics']);
		}

		// Time online top 10.
		$temp = cache_get_data('stats_total_time_members', 600);
		$members_result = $db->query('', '<commit_msg>added a subs function for top topic starters. Signed-off-by:<NAME> <<EMAIL>><commit_after><?php

/**
 * @name      ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
 *
 * This software is a derived product, based on:
 *
 * Simple Machines Forum (SMF)
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
 *
 * @version 1.0 Alpha
 *
 * Provide a display for forum statistics
 *
 */

if (!defined('ELK'))
	die('No access...');

/**
 * Statistics Controller
 */
class Stats_Controller extends Action_Controller
{
	/**
	 * Entry point for this class.
	 *
	 * @see Action_Controller::action_index()
	 */
	public function action_index()
	{
		// Call the right method... wait, we only know how to do
		// one thing (and do it well! :P)
		$this->action_stats();
	}

	/**
	 * Display some useful/interesting board statistics.
	 *
	 * gets all the statistics in order and puts them in.
	 * uses the Stats template and language file. (and main sub template.)
	 * requires the view_stats permission.
	 * accessed from ?action=stats.
	 */
	public function action_stats()
	{
		global $txt, $scripturl, $modSettings, $context;

		$db = database();

		isAllowedTo('view_stats');

		// Page disabled - redirect them out
		if (empty($modSettings['trackStats']))
			fatal_lang_error('feature_disabled', true);

		if (!empty($_REQUEST['expand']))
		{
			$context['robot_no_index'] = true;

			$month = (int) substr($_REQUEST['expand'], 4);
			$year = (int) substr($_REQUEST['expand'], 0, 4);
			if ($year > 1900 && $year < 2200 && $month >= 1 && $month <= 12)
				$_SESSION['expanded_stats'][$year][] = $month;
		}
		elseif (!empty($_REQUEST['collapse']))
		{
			$context['robot_no_index'] = true;

			$month = (int) substr($_REQUEST['collapse'], 4);
			$year = (int) substr($_REQUEST['collapse'], 0, 4);
			if (!empty($_SESSION['expanded_stats'][$year]))
				$_SESSION['expanded_stats'][$year] = array_diff($_SESSION['expanded_stats'][$year], array($month));
		}

		// Handle the XMLHttpRequest.
		if (isset($_REQUEST['xml']))
		{
			// Collapsing stats only needs adjustments of the session variables.
			if (!empty($_REQUEST['collapse']))
				obExit(false);

			$context['sub_template'] = 'stats';
			getDailyStats('YEAR(date) = {int:year} AND MONTH(date) = {int:month}', array('year' => $year, 'month' => $month));
			$context['yearly'][$year]['months'][$month]['date'] = array(
				'month' => sprintf('%02d', $month),
				'year' => $year,
			);
			return;
		}

		loadLanguage('Stats');
		loadTemplate('Stats');

		// Just a lil' help from our friend :P
		require_once(SUBSDIR . '/Stats.subs.php');

		// Build the link tree......
		$context['linktree'][] = array(
			'url' => $scripturl . '?action=stats',
			'name' => $txt['stats_center']
		);
		$context['page_title'] = $context['forum_name'] . ' - ' . $txt['stats_center'];

		$context['show_member_list'] = allowedTo('view_mlist');

		// Get averages...
		$averages = getAverages();
		// This would be the amount of time the forum has been up... in days...
		$total_days_up = ceil((time() - strtotime($averages['date'])) / (60 * 60 * 24));

		$context['average_posts'] = comma_format(round($averages['posts'] / $total_days_up, 2));
		$context['average_topics'] = comma_format(round($averages['topics'] / $total_days_up, 2));
		$context['average_members'] = comma_format(round($averages['registers'] / $total_days_up, 2));
		$context['average_online'] = comma_format(round($averages['most_on'] / $total_days_up, 2));
		$context['average_hits'] = comma_format(round($averages['hits'] / $total_days_up, 2));

		$context['num_hits'] = comma_format($averages['hits'], 0);

		// How many users are online now.
		$context['users_online'] = onlineCount();

		// Statistics such as number of boards, categories, etc.
		$context['num_boards'] = numBoards();
		$context['num_categories'] = numCategories();

		// Format the numbers nicely.
		$context['users_online'] = comma_format($context['users_online']);
		$context['num_boards'] = comma_format($context['num_boards']);
		$context['num_categories'] = comma_format($context['num_categories']);

		$context['num_members'] = comma_format($modSettings['totalMembers']);
		$context['num_posts'] = comma_format($modSettings['totalMessages']);
		$context['num_topics'] = comma_format($modSettings['totalTopics']);
		$context['most_members_online'] = array(
			'number' => comma_format($modSettings['mostOnline']),
			'date' => relativeTime($modSettings['mostDate'])
		);
		$context['latest_member'] = &$context['common_stats']['latest_member'];

		// Male vs. female ratio - let's calculate this only every four minutes.
		if (($context['gender'] = cache_get_data('stats_gender', 240)) == null)
		{
			$context['gender'] = genderRatio();

			// Set these two zero if the didn't get set at all.
			if (empty($context['gender']['males']))
				$context['gender']['males'] = 0;
			if (empty($context['gender']['females']))
				$context['gender']['females'] = 0;

			// Try and come up with some "sensible" default states in case of a non-mixed board.
			if ($context['gender']['males'] == $context['gender']['females'])
				$context['gender']['ratio'] = '1:1';
			elseif ($context['gender']['males'] == 0)
				$context['gender']['ratio'] = '0:1';
			elseif ($context['gender']['females'] == 0)
				$context['gender']['ratio'] = '1:0';
			elseif ($context['gender']['males'] > $context['gender']['females'])
				$context['gender']['ratio'] = round($context['gender']['males'] / $context['gender']['females'], 1) . ':1';
			elseif ($context['gender']['females'] > $context['gender']['males'])
				$context['gender']['ratio'] = '1:' . round($context['gender']['females'] / $context['gender']['males'], 1);

			cache_put_data('stats_gender', $context['gender'], 240);
		}

		$date = strftime('%Y-%m-%d', forum_time(false));

		// Members online so far today.
		$context['online_today'] = mostOnline($date);
		$context['online_today'] = comma_format((int) $context['online_today']);

		// Poster top 10.
		$context['top_posters'] = topPosters();
	
		foreach ($context['top_posters'] as $i => $poster)
		{
			$context['top_posters'][$i]['post_percent'] = round(($poster['num_posts'] * 100) / $max_num_posts);
			$context['top_posters'][$i]['num_posts'] = comma_format($context['top_posters'][$i]['num_posts']);
		}

		// Board top 10.
		$context['top_boards'] = topBoards();

		foreach ($context['top_boards'] as $i => $board)
		{
			$context['top_boards'][$i]['post_percent'] = round(($board['num_posts'] * 100) / $max_num_posts);
			$context['top_boards'][$i]['num_posts'] = comma_format($context['top_boards'][$i]['num_posts']);
		}

		// Topic replies top 10.
		$context['top_topics_replies'] = topTopicReplies();
	
		foreach ($context['top_topics_replies'] as $i => $topic)
		{
			$context['top_topics_replies'][$i]['post_percent'] = round(($topic['num_replies'] * 100) / $max_num_replies);
			$context['top_topics_replies'][$i]['num_replies'] = comma_format($context['top_topics_replies'][$i]['num_replies']);
		}

		// Topic views top 10.
		$context['top_topics_views'] = topTopicViews();

		foreach ($context['top_topics_views'] as $i => $topic)
		{
			$context['top_topics_views'][$i]['post_percent'] = round(($topic['num_views'] * 100) / $max_num_views);
			$context['top_topics_views'][$i]['num_views'] = comma_format($context['top_topics_views'][$i]['num_views']);
		}

		// Topic poster top 10.
		$context['top_starters'] = topTopicStarter();


		// Time online top 10.
		$temp = cache_get_data('stats_total_time_members', 600);
		$members_result = $db->query('', '
{'fraction_non_alphanumeric': 0.20374626522638473, 'fraction_numerical': 0.010744656400827396, 'mean_word_length': 4.902000678195999, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6875239', 'n_tokens_mistral': 6596, 'n_tokens_neox': 5770, 'n_words': 1164}