Completed
Push — master ( 5ae889...256588 )
by Angus
08:21
created

Tracker_Admin_Model::updateLatestChapters()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 113
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 61
nc 7
nop 0
dl 0
loc 113
ccs 0
cts 53
cp 0
crap 72
rs 5.2676
c 0
b 0
f 0

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