Completed
Push — master ( 84f8b9...29b389 )
by Angus
08:20
created

Tracker_Admin_Model::updateLatestChapters()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 72
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 37
nc 3
nop 0
dl 0
loc 72
rs 9.102
c 0
b 0
f 0
ccs 0
cts 40
cp 0
crap 12

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1); defined('BASEPATH') OR exit('No direct script access allowed');
2
3
class Tracker_Admin_Model extends Tracker_Base_Model {
4 127
	public function __construct() {
5 127
		parent::__construct();
6 127
	}
7
8
	/**
9
	 * Checks for any series that haven't updated in 16 hours and updates them.
10
	 * This is ran every 4 hours via a cron job.
11
	 */
12
	public function updateLatestChapters() {
13
		// region $query = Get all titles ready to update;
14
		// @formatter:off
15
		$query = $this->db
16
			->select('
17
				tracker_titles.id as title_id,
18
				tracker_titles.title,
19
				tracker_titles.title_url,
20
				tracker_titles.status,
21
				tracker_sites.site,
22
				tracker_sites.site_class,
23
				tracker_sites.status,
24
				tracker_titles.latest_chapter,
25
				tracker_titles.last_updated,
26
				from_unixtime(MAX(auth_users.last_login)) AS timestamp
27
			')
28
			->from('tracker_titles')
29
			->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
30
			->join('tracker_chapters', 'tracker_titles.id = tracker_chapters.title_id', 'left')
31
			->join('auth_users', 'tracker_chapters.user_id = auth_users.id', 'left')
32
			->where('tracker_sites.status', 'enabled')
33
			->group_start()
34
				//Check if title is marked as on-going...
35
				->where('tracker_titles.status', 0)
36
				//AND matches one of where queries below
37
				->group_start()
38
					//Then check if it's NULL (only occurs for new series)
39
					->where('latest_chapter', NULL)
40
					//OR if it hasn't updated within the past 12 hours AND isn't a custom update site
41
					->or_group_start()
42
						->where('tracker_sites.use_custom', 'N')
43
						->where('last_checked < DATE_SUB(NOW(), INTERVAL 12 HOUR)')
44
					->group_end()
45
					//OR it is a custom update site, has more than one follower and hasn't updated within the past 72 hours.
46
					->or_group_start()
47
						->where('tracker_titles.id IN (
48
							SELECT title_id
49
							FROM tracker_chapters
50
							GROUP BY title_id
51
							HAVING COUNT(title_id) > 1
52
						)', NULL, FALSE)
53
54
						->where('last_checked < DATE_SUB(NOW(), INTERVAL 72 HOUR)')
55
					->group_end()
56
					//OR it is a custom update site and hasn't updated within the past 120 hours (5 days)
57
					->or_where('last_checked < DATE_SUB(NOW(), INTERVAL 120 HOUR)')
58
				->group_end()
59
			->group_end()
60
			->or_group_start()
61
				//Check if title is marked as complete...
62
				->where('tracker_titles.status', 1)
63
				//Then check if it hasn't updated within the past week
64
				->where('last_checked < DATE_SUB(NOW(), INTERVAL 1 WEEK)')
65
			->group_end()
66
			//Status 2 (One-shot) & 255 (Ignore) are both not updated intentionally.
67
			->group_by('tracker_titles.id, tracker_chapters.active')
68
			//Check if the series is actually being tracked by someone
69
			->having('timestamp IS NOT NULL')
70
			//AND if it's currently marked as active by the user
71
			->having('tracker_chapters.active', 'Y')
72
			//AND if they have been active in the last 120 hours (5 days)
73
			->having('timestamp > DATE_SUB(NOW(), INTERVAL 120 HOUR)')
74
			->order_by('tracker_titles.title', 'ASC');
75
		// endregion
76
		$query = $query->get();
77
78
		if($query->num_rows() > 0) {
79
			foreach ($query->result() as $row) {
80
				$this->handleUpdate($row);
81
			}
82
		}
83
	}
84
85
	/**
86
	 * Intended to be only used as a quick way to update all series on a site after a bug.
87
	 *
88
	 * @param string      $site
89
	 * @param null|string $last_checked
90
	 */
91
	public function updateAllTitlesBySite(string $site, ?string $last_checked = NULL) {
92
		// region $query = Get all titles by $site;
93
		// @formatter:off
94
		$query = $this->db
95
			->select('
96
				tracker_titles.id as title_id,
97
				tracker_titles.title,
98
				tracker_titles.title_url,
99
				tracker_titles.status,
100
				tracker_sites.site,
101
				tracker_sites.site_class,
102
				tracker_sites.status,
103
				tracker_titles.latest_chapter,
104
				tracker_titles.last_updated,
105
				from_unixtime(MAX(auth_users.last_login)) AS timestamp
106
			')
107
			->from('tracker_titles')
108
			->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
109
			->join('tracker_chapters', 'tracker_titles.id = tracker_chapters.title_id', 'left')
110
			->join('auth_users', 'tracker_chapters.user_id = auth_users.id', 'left')
111
			->where('tracker_sites.status', 'enabled')
112
			->where('tracker_sites.site_class', $site)
113
			->group_start()
114
				//Check if title is marked as on-going...
115
				->where('tracker_titles.status', 0)
116
				//Check if title is marked as complete...
117
				->or_where('tracker_titles.status', 1)
118
			->group_end()
119
			//Status 2 (One-shot) & 255 (Ignore) are both not updated intentionally.
120
			->group_by('tracker_titles.id, tracker_chapters.active')
121
			//Check if the series is actually being tracked by someone
122
			->having('timestamp IS NOT NULL')
123
			//AND if it's currently marked as active by the user
124
			->having('tracker_chapters.active', 'Y')
125
			//AND if they have been active in the last 120 hours (5 days)
126
			->having('timestamp > DATE_SUB(NOW(), INTERVAL 120 HOUR)')
127
			->order_by('tracker_titles.last_checked', 'ASC');
128
		// @formatter:on
129
		if(!is_null($last_checked)) {
130
			$query = $query->where('tracker_titles.last_checked >', $last_checked);
131
		}
132
		// endregion
133
		$query = $query->get();
134
135
		if($query->num_rows() > 0) {
136
			foreach ($query->result() as $row) {
137
				$this->handleUpdate($row);
138
			}
139
		}
140
	}
141
142
	private function handleUpdate(object $row) : void{
143
		print "> {$row->title} <{$row->site_class} - {$row->title_url}> | <{$row->title_id}>"; //Print this prior to doing anything so we can more easily find out if something went wrong
144
		$titleData = $this->sites->{$row->site_class}->getTitleData($row->title_url);
145
		if(is_array($titleData) && (!is_null($titleData['latest_chapter']) || $this->sites->{$row->site_class}->canHaveNoChapters)) {
146
			if(count($titleData) === 3) {
147
				// Normal update.
148
149
				//FIXME: "At the moment" we don't seem to be doing anything with TitleData['last_updated'].
150
				//       Should we even use this? Y/N
151
				if($this->Tracker->title->updateByID((int) $row->title_id, $titleData['latest_chapter'])) {
152
					//Make sure last_checked is always updated on successful run.
153
					//CHECK: Is there a reason we aren't just doing this in updateByID?
154
					$this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE)
155
					         ->where('id', $row->title_id)
156
					         ->update('tracker_titles');
157
158
					print " - ({$titleData['latest_chapter']})\n";
159
				} else {
160
					log_message('error', "{$row->title} failed to update successfully");
161
162
					print " - Something went wrong?\n";
163
				}
164
			} else {
165
				// No chapters were returned, but site allows this.
166
				if($this->Tracker->title->updateByID((int) $row->title_id, NULL)) {
167
					//Make sure last_checked is always updated on successful run.
168
					//CHECK: Is there a reason we aren't just doing this in updateByID?
169
					$this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE)
170
					         ->where('id', $row->title_id)
171
					         ->update('tracker_titles');
172
173
					print " - (No chapters found?)\n";
174
				} else {
175
					log_message('error', "{$row->title} failed to update successfully");
176
177
					print " - Something went wrong?\n";
178
				}
179
			}
180
		} else {
181
			log_message('error', "{$row->title} failed to update successfully");
182
			$this->Tracker->title->updateFailedChecksByID((int) $row->title_id);
183
184
			print " - FAILED TO PARSE\n";
185
		}
186
	}
187
188
	/**
189
	 * Checks for any sites which support custom updating (usually via following lists) and updates them.
190
	 * This is run hourly.
191
	 */
192
	public function updateCustom() {
193
		$query = $this->db->select('*')
194
		                  ->from('tracker_sites')
195
		                  ->where('status', 'enabled')
196
		                  ->where('tracker_sites.use_custom', 'Y')
197
		                  ->get();
198
199
		$sites = $query->result_array();
200
		foreach ($sites as $site) {
201
			$siteClass = $this->sites->{$site['site_class']};
202
			if($titleDataList = $siteClass->doCustomUpdate()) {
203
				foreach ($titleDataList as $titleURL => $titleData) {
204
					$titleURL = (string) $titleURL; //Number only keys get converted to int for some reason, so we need to fix that.
205
					print "> {$titleData['title']} <{$site['site_class']}>"; //Print this prior to doing anything so we can more easily find out if something went wrong
206
					if(is_array($titleData) && !is_null($titleData['latest_chapter'])) {
207
						if($dbTitleData = $this->Tracker->title->getID($titleURL, (int) $site['id'], FALSE, TRUE)) {
208
							if($this->sites->{$site['site_class']}->doCustomCheck($dbTitleData['latest_chapter'], $titleData['latest_chapter'])) {
209
								$titleID = $dbTitleData['id'];
210
								if($this->Tracker->title->updateByID((int) $titleID, $titleData['latest_chapter'])) {
211
									//Make sure last_checked is always updated on successful run.
212
									//CHECK: Is there a reason we aren't just doing this in updateByID?
213
									$this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE)
214
									         ->where('id', $titleID)
215
									         ->update('tracker_titles');
216
217
									print " - ({$titleData['latest_chapter']})\n";
218
								} else {
219
									print " - Title doesn't exist? ($titleID)\n";
220
								}
221
							} else {
222
								print " - Failed Check (DB: '{$dbTitleData['latest_chapter']}' || UPDATE: '{$titleData['latest_chapter']}')\n";
223
							}
224
						} else {
225
							if($siteClass->customType === 1) {
226
								//We only need to log if following page is missing title, not latest releases
227
								log_message('error', "CUSTOM: {$titleData['title']} - {$site['site_class']} || Title does not exist in DB??");
228
								print " - Title doesn't currently exist in DB? Maybe different language or title stub change? ($titleURL)\n";
229
							} else {
230
								print " - Title isn't currently tracked.\n";
231
							}
232
						}
233
					} else {
234
						log_message('error', "CUSTOM: {$titleData['title']} - {$site['site_class']} failed to custom update successfully");
235
						print " - FAILED TO PARSE\n";
236
					}
237
				}
238
			}
239
		}
240
	}
241
242
	public function refollowCustom() {
243
		$query = $this->db->select('tracker_titles.id, tracker_titles.title_url, tracker_sites.site_class')
244
		                  ->from('tracker_titles')
245
		                  ->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
246
		                  ->where('tracker_titles.followed','N')
247
		                  ->where('tracker_titles !=', '255')
248
		                  ->where('tracker_sites.status', 'enabled')
249
		                  ->where('tracker_sites.use_custom', 'Y')
250
		                  ->get();
251
252
		if($query->num_rows() > 0) {
253
			foreach($query->result() as $row) {
254
				$titleData = $this->Tracker->sites->{$row->site_class}->getTitleData($row->title_url, TRUE);
255
256
				if($titleData) {
257
					$titleData = array_intersect_key($titleData, array_flip(['followed']));
258
259
					if(!empty($titleData)) {
260
						$this->db->set($titleData)
261
						         ->where('id', $row->id)
262
						         ->update('tracker_titles');
263
264
						print "> {$row->site_class}:{$row->id}:{$row->title_url} FOLLOWED\n";
265
					} else {
266
						print "> {$row->site_class}:{$row->id}:{$row->title_url} FAILED (NO FOLLOWED)\n";
267
					}
268
				} else {
269
					log_message('error', "getTitleData failed for: {$row->site_class} | {$row->title_url}");
270
					print "> {$row->site_class}:{$row->id}:{$row->title_url} FAILED (NO TITLEDATA)\n";
271
				}
272
			}
273
		}
274
	}
275
276
	/**
277
	 * Checks every series to see if title has changed, and update if so.
278
	 * This is ran once a month via a cron job
279
	 */
280
	public function updateTitles() {
281
		// @formatter:off
282
		$query = $this->db
283
			->select('
284
				tracker_titles.id,
285
				tracker_titles.title,
286
				tracker_titles.title_url,
287
				tracker_titles.status,
288
				tracker_sites.site,
289
				tracker_sites.site_class,
290
				tracker_sites.status,
291
				tracker_titles.latest_chapter,
292
				tracker_titles.last_updated
293
			')
294
			->from('tracker_titles')
295
			->join('tracker_sites', 'tracker_sites.id = tracker_titles.site_id', 'left')
296
			->where('tracker_sites.status', 'enabled')
297
298
			->group_by('tracker_titles.id')
299
			->order_by('tracker_titles.title', 'ASC')
300
			->get();
301
		// @formatter:on
302
303
		if($query->num_rows() > 0) {
304
			foreach ($query->result() as $row) {
305
				print "> {$row->title} <{$row->site_class}>"; //Print this prior to doing anything so we can more easily find out if something went wrong
306
				$titleData = $this->sites->{$row->site_class}->getTitleData($row->title_url);
307
				if($titleData['title'] && is_array($titleData) && !is_null($titleData['latest_chapter'])) {
308
					if($titleData['title'] !== $row->title) {
309
						$this->db->set('title', $titleData['title'])
310
						         ->where('id', $row->id)
311
						         ->update('tracker_titles');
312
						//TODO: Add to history somehow?
313
						print " - NEW TITLE ({$titleData['title']})\n";
314
					} else {
315
						print " - TITLE NOT CHANGED\n";
316
					}
317
318
					//We might as well try to update as well.
319
					if($this->Tracker->title->updateByID((int) $row->id, $titleData['latest_chapter'])) {
320
						$this->db->set('last_checked', 'CURRENT_TIMESTAMP', FALSE)
321
						         ->where('id', $row->id)
322
						         ->update('tracker_titles');
323
					}
324
				} else {
325
					log_message('error', "{$row->title} failed to update title successfully");
326
					print " - FAILED TO PARSE\n";
327
				}
328
			}
329
		}
330
	}
331
332
	public function incrementRequests() : void {
333
		$temp_now = new DateTime();
334
		$temp_now->setTimezone(new DateTimeZone('America/New_York'));
335
		$date = $temp_now->format('Y-m-d');
336
337
		$query = $this->db->select('1')
338
		                  ->from('site_stats')
339
		                  ->where('date', $date)
340
		                  ->get();
341
342
		if($query->num_rows() > 0) {
343
			$this->db->set('total_requests', 'total_requests+1', FALSE)
344
			         ->where('date', $date)
345
			         ->update('site_stats');
346
		} else {
347
			$this->db->insert('site_stats', [
348
				'date'           => $date,
349
				'total_requests' => 1
350
			]);
351
		}
352
	}
353
354
	public function getNextUpdateTime(string $format = "%H:%I:%S") : string {
355
		$temp_now = new DateTime();
356
		$temp_now->setTimezone(new DateTimeZone('America/New_York'));
357
		$temp_now_formatted = $temp_now->format('Y-m-d H:i:s');
358
359
		//NOTE: PHP Bug: DateTime:diff doesn't play nice with setTimezone, so we need to create another DT object
360
		$now         = new DateTime($temp_now_formatted);
361
		$future_date = new DateTime($temp_now_formatted);
362
		$now_hour    = (int) $now->format('H');
363
		if($now_hour < 4) {
364
			//Time until 4am
365
			$future_date->setTime(4, 00);
366
		} elseif($now_hour < 8) {
367
			//Time until 8am
368
			$future_date->setTime(8, 00);
369
		} elseif($now_hour < 12) {
370
			//Time until 12pm
371
			$future_date->setTime(12, 00);
372
		} elseif($now_hour < 16) {
373
			//Time until 4pm
374
			$future_date->setTime(16, 00);
375
		} elseif($now_hour < 20) {
376
			//Time until 8pm
377
			$future_date->setTime(20, 00);
378
		} else {
379
			//Time until 12am
380
			$future_date->setTime(00, 00);
381
			$future_date->add(new DateInterval('P1D'));
382
		}
383
384
		$interval = $future_date->diff($now);
385
		return $interval->format($format);
386
	}
387
}
388