Passed
Push — master ( acd6da...ff03b4 )
by Daimona
01:34
created

PageRiconferma::getSupportCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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