Passed
Push — master ( c20c86...5d9847 )
by Daimona
01:48
created

PageRiconferma::getCreationTimestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php declare( strict_types=1 );
2
3
namespace BotRiconferme\Wiki\Page;
4
5
use BotRiconferme\Config;
6
use BotRiconferme\Exception\MissingPageException;
7
use BotRiconferme\Exception\TaskException;
8
use BotRiconferme\Message;
9
use BotRiconferme\Wiki\User;
10
11
/**
12
 * Represents a single riconferma page
13
 */
14
class PageRiconferma extends Page {
15
	// Sections of the page, value = section number. Loaded in self::defineSections
16
	private $supportSection;
17
	private $opposeSection;
18
	/** @var array Counts of votes for each section */
19
	private $sectionCounts = [];
20
21
	// Possible outcomes of a vote
22
	public const OUTCOME_OK = 0;
23
	public const OUTCOME_FAIL_VOTES = 1;
24
	public const OUTCOME_NO_QUOR = 2;
25
	public const OUTCOME_FAIL = self::OUTCOME_FAIL_VOTES | self::OUTCOME_NO_QUOR;
26
27
	// Values depending on bureaucracy
28
	public const REQUIRED_OPPOSE = 15;
29
	public const SIMPLE_DURATION = 7;
30
	public const VOTE_DURATION = 14;
31
	public const SUCCESS_RATIO = 2 / 3;
32
33
	/**
34
	 * Define the numbers of the support and oppose sections. These are lazy-loaded
35
	 * because they can vary depending on whether the page is a vote, which is relatively
36
	 * expensive to know since it requires parsing the content of the page.
37
	 */
38
	private function defineSections() {
39
		$this->supportSection = $this->isVote() ? 3 : 0;
40
		$this->opposeSection = $this->isVote() ? 4 : 3;
41
	}
42
43
	/**
44
	 * Get the name of the user from the title
45
	 *
46
	 * @return User
47
	 */
48
	public function getUser() : User {
49
		$name = explode( '/', $this->title )[2];
50
		return new User( $name, $this->wiki );
51
	}
52
53
	/**
54
	 * Returns the progressive number in the title
55
	 *
56
	 * @return int
57
	 */
58
	public function getNum() : int {
59
		$bits = explode( '/', $this->getTitle() );
60
		return intval( end( $bits ) );
61
	}
62
63
	/**
64
	 * Get the last part of the title as Username/Num
65
	 *
66
	 * @return string
67
	 */
68
	public function getUserNum() : string {
69
		return explode( '/', $this->getTitle(), 3 )[2];
70
	}
71
72
	/**
73
	 * Get the amount of opposing votes
74
	 *
75
	 * @return int
76
	 */
77
	public function getOpposingCount() : int {
78
		$this->defineSections();
79
		return $this->getCountForSection( $this->opposeSection );
80
	}
81
82
	/**
83
	 * Get the amount support votes
84
	 *
85
	 * @return int
86
	 * @throws \BadMethodCallException
87
	 */
88
	public function getSupportCount() : int {
89
		if ( !$this->isVote() ) {
90
			throw new \BadMethodCallException( 'Cannot get support for a non-vote page.' );
91
		}
92
		$this->defineSections();
93
		return $this->getCountForSection( $this->supportSection );
94
	}
95
96
	/**
97
	 * Count the votes in the given section
98
	 *
99
	 * @param int $secNum
100
	 * @return int
101
	 */
102
	protected function getCountForSection( int $secNum ) : int {
103
		if ( !isset( $this->sectionCounts[ $secNum ] ) ) {
104
			$content = $this->wiki->getPageContent( $this->title, $secNum );
105
			// Let's hope that this is good enough...
106
			$this->sectionCounts[$secNum] = preg_match_all( "/^\# *(?![# *:]|\.\.\.$)/m", $content );
107
		}
108
		return $this->sectionCounts[$secNum];
109
	}
110
111
	/**
112
	 * Gets the quorum used for the current page
113
	 *
114
	 * @return int
115
	 */
116
	protected function getQuorum() : int {
117
		$reg = "!soddisfare il \[\[[^|\]]+\|quorum]] di '''(\d+) voti'''!";
118
		return intval( $this->getMatch( $reg )[1] );
119
	}
120
121
	/**
122
	 * Whether this page has enough opposing votes
123
	 *
124
	 * @return bool
125
	 */
126
	public function hasOpposition() : bool {
127
		return $this->getOpposingCount() >= self::REQUIRED_OPPOSE;
128
	}
129
130
	/**
131
	 * Gets the outcome for the vote
132
	 *
133
	 * @return int One of the OUTCOME_* constants
134
	 */
135
	public function getOutcome() : int {
136
		if ( !$this->isVote() ) {
137
			return self::OUTCOME_OK;
138
		}
139
		$totalVotes = $this->getOpposingCount() + $this->getSupportCount();
140
141
		if ( $this->getSupportCount() < $this->getQuorum() ) {
142
			$ret = self::OUTCOME_NO_QUOR;
143
		} elseif ( $this->getSupportCount() < self::SUCCESS_RATIO * $totalVotes ) {
144
			$ret = self::OUTCOME_FAIL_VOTES;
145
		} else {
146
			$ret = self::OUTCOME_OK;
147
		}
148
		return $ret;
149
	}
150
151
	/**
152
	 * Get the result text for the page itself
153
	 *
154
	 * @return string
155
	 * @throws \BadMethodCallException
156
	 * @throws \LogicException
157
	 */
158
	public function getOutcomeText() : string {
159
		if ( !$this->isVote() ) {
160
			throw new \BadMethodCallException( 'No need for an outcome text.' );
161
		}
162
163
		$text = sprintf(
164
			' Con %d voti a favore e %d contrari',
165
			$this->getSupportCount(),
166
			$this->getOpposingCount()
167
		);
168
		$user = $this->getUser();
169
170
		switch ( $this->getOutcome() ) {
171
			case self::OUTCOME_OK:
172
				$text .= " $user viene riconfermato amministratore.";
173
				break;
174
			/** @noinspection PhpMissingBreakStatementInspection */
175
			case self::OUTCOME_NO_QUOR:
176
				$text .= ', non raggiungendo il quorum,';
177
				// Fall-through intended
178
			case self::OUTCOME_FAIL:
179
				$text .= " $user non viene riconfermato amministratore.";
180
				break;
181
			default:
182
				throw new \LogicException( 'Invalid outcome: ' . $this->getOutcome() );
183
		}
184
		return $text;
185
	}
186
187
	/**
188
	 * Whether this page is a vote
189
	 *
190
	 * @return bool
191
	 */
192
	public function isVote() : bool {
193
		$sectionReg = '/<!-- SEZIONE DA UTILIZZARE PER/';
194
		return !$this->matches( $sectionReg );
195
	}
196
197
	/**
198
	 * Get the timestamp of the creation of the page
199
	 *
200
	 * @return int
201
	 */
202
	public function getCreationTimestamp() : int {
203
		return $this->wiki->getPageCreationTS( $this->title );
204
	}
205
206
	/**
207
	 * Get the end time
208
	 *
209
	 * @return int
210
	 */
211
	public function getEndTimestamp() : int {
212
		if ( $this->isVote() ) {
213
			$reg = "!La votazione ha inizio il.+ alle ore ([\d:]+) e ha termine il (.+) alla stessa ora!";
214
			list( , $hours, $day ) = $this->getMatch( $reg );
215
			$day = preg_replace( '![^\d \w]!', '', $day );
216
			return Message::getTimestampFromLocalTime( "$day $hours" );
217
		} else {
218
			return $this->getCreationTimestamp() + 60 * 60 * 24 * self::SIMPLE_DURATION;
219
		}
220
	}
221
}
222