Completed
Push — master ( 89a822...3faa1c )
by Angus
03:22
created

Tracker_Admin_Model::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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