Passed
Pull Request — master (#129)
by
unknown
04:25
created

AccountUpdater::setIsInvalidUnknown()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 7
rs 10
1
<?php
2
/**
3
 * Created for IG Monitoring.
4
 * User: jakim <[email protected]>
5
 * Date: 12.06.2018
6
 */
7
8
namespace app\components\updaters;
9
10
11
use app\components\instagram\models\Account;
12
use app\components\traits\NextUpdateCalculatorTrait;
13
use app\components\traits\SaveModelTrait;
14
use app\components\traits\SetAccountTrait;
15
use app\models\AccountStats;
16
use DateTime;
17
use Throwable;
18
use yii\base\Component;
19
use yii\db\Expression;
20
use yii\helpers\ArrayHelper;
21
use function count;
22
23
class AccountUpdater extends Component
24
{
25
    use SaveModelTrait, SetAccountTrait, NextUpdateCalculatorTrait;
0 ignored issues
show
Bug introduced by
The trait app\components\traits\SaveModelTrait requires the property $errors which is not provided by app\components\updaters\AccountUpdater.
Loading history...
26
27
    private $postStats;
28
29
    public function init()
30
    {
31
        parent::init();
32
        $this->throwExceptionIfAccountIsNotSet();
33
    }
34
35
    /**
36
     * Update username and instagram_id.
37
     *
38
     * @param \app\components\instagram\models\Account $account
39
     * @return $this
40
     */
41
    public function setIdents(Account $account)
42
    {
43
        $this->account->username = $account->username;
44
        $this->account->instagram_id = (string)$account->id;
45
46
        return $this;
47
    }
48
49
    /**
50
     * @param \app\components\instagram\models\Account $account
51
     * @return $this
52
     */
53
    public function setDetails(Account $account)
54
    {
55
        $this->setIdents($account);
56
        $this->account->profile_pic_url = $account->profilePicUrl;
57
        $this->account->full_name = $account->fullName;
58
        $this->account->biography = $account->biography;
59
        $this->account->external_url = $account->externalUrl;
60
        $this->account->is_verified = $account->isVerified;
61
        $this->account->is_business = $account->isBusiness;
62
        $this->account->business_category = $account->businessCategory;
63
64
        return $this;
65
    }
66
67
    public function setMonitoring($proxyId = null)
68
    {
69
        $this->account->monitoring = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $monitoring was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
70
        $this->account->proxy_id = $proxyId;
71
72
        return $this;
73
    }
74
75
    public function setIsValid()
76
    {
77
        $this->account->is_valid = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $is_valid was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
78
        $this->account->invalidation_count = 0;
79
        $this->account->invalidation_type_id = null;
80
81
        return $this;
82
    }
83
84
    public function setIsInvalid(?int $invalidationType = null)
85
    {
86
        $this->account->is_valid = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $is_valid was declared of type boolean, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
87
        $this->account->invalidation_count = (int)$this->account->invalidation_count + 1;
88
        $this->account->invalidation_type_id = $invalidationType;
89
90
        return $this;
91
    }
92
93
    public function setIsInvalidUnknown(string $message = null)
94
    {
95
        $this->account->is_valid = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $is_valid was declared of type boolean, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
96
        $this->account->invalidation_count = (int)$this->account->invalidation_count + 1;
97
        $this->account->last_invalidation_unknown = $message;
98
99
        return $this;
100
    }
101
102
    /**
103
     * If true, then will be automatically calculate from invalidation_count
104
     *
105
     * @param true|int|null $interval
106
     * @return $this
107
     */
108
    public function setNextStatsUpdate($interval = 24)
109
    {
110
        $this->account->update_stats_after = $this->getNextUpdateDate($this->account, $interval);
0 ignored issues
show
Bug introduced by
It seems like $interval can also be of type true; however, parameter $interval of app\components\updaters\...er::getNextUpdateDate() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

110
        $this->account->update_stats_after = $this->getNextUpdateDate($this->account, /** @scrutinizer ignore-type */ $interval);
Loading history...
111
112
        return $this;
113
    }
114
115
    /**
116
     * Direct account statistics.
117
     *
118
     * @param \app\components\instagram\models\Account $account
119
     * @param array|\app\components\instagram\models\Post[] $posts
120
     * @param bool $createHistory
121
     * @return $this
122
     */
123
    public function setStats(Account $account, array $posts, bool $createHistory = true)
124
    {
125
        $this->account->touch('stats_updated_at');
0 ignored issues
show
Bug introduced by
The method touch() does not exist on app\models\Account. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
        $this->account->/** @scrutinizer ignore-call */ 
126
                        touch('stats_updated_at');
Loading history...
126
        if ($this->account->media === null || $this->statsNeedUpdate($account, $posts)) {
127
            $postsStats = $this->postsStats($account, $posts);
128
            $this->account->media = $account->media;
129
            $this->account->follows = $account->follows;
130
            $this->account->followed_by = $account->followedBy;
131
            $this->account->er = $postsStats['er'];
132
            $this->account->avg_likes = $postsStats['avg_likes'];
133
            $this->account->avg_comments = $postsStats['avg_comments'];
134
135
            if ($lastPost = ArrayHelper::getValue($posts, '0')) {
136
                try {
137
                    $takenAt = (new DateTime('@' . ArrayHelper::getValue($lastPost, 'takenAt')))->format('Y-m-d H:i:s');
138
                    $this->account->last_post_taken_at = $takenAt;
139
                } catch (Throwable $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
140
                }
141
142
            }
143
144
            if ($createHistory) {
145
                $this->createHistory();
146
            }
147
        }
148
149
        return $this;
150
    }
151
152
    /**
153
     * @throws \yii\web\ServerErrorHttpException
154
     */
155
    public function save()
156
    {
157
        $this->saveModel($this->account);
158
    }
159
160
    protected function createHistory()
161
    {
162
        // dirty fix - avoiding duplicates
163
        // the possibility of occurrence if the account is monitored and appears as a tagged/mentioned in the post
164
        AccountStats::deleteAll(['AND',
165
            ['account_id' => $this->account->id],
166
            new Expression('DATE(created_at)=DATE(NOW())'),
167
        ]);
168
        $accountStats = new AccountStats([
169
            'account_id' => $this->account->id,
170
            'media' => $this->account->media,
171
            'follows' => $this->account->follows,
172
            'followed_by' => $this->account->followed_by,
173
            'er' => $this->account->er,
174
            'avg_likes' => $this->account->avg_likes,
175
            'avg_comments' => $this->account->avg_comments,
176
        ]);
177
        $this->saveModel($accountStats);
178
179
        return $accountStats;
180
    }
181
182
    private function statsNeedUpdate(Account $account, array $posts): bool
183
    {
184
        $res = $this->account->media != $account->media ||
185
            $this->account->follows != $account->follows ||
186
            $this->account->followed_by != $account->followedBy;
187
        if ($res === false) {
188
            $postStats = $this->postsStats($account, $posts);
189
            $res = $this->account->er != $postStats['er'] ||
190
                $this->account->avg_likes != $postStats['avg_likes'] ||
191
                $this->account->avg_comments != $postStats['avg_comments'];
192
        }
193
194
        return $res;
195
    }
196
197
    /**
198
     * Statistics calculated from posts (engagement, avg_likes, avg_comments).
199
     *
200
     * @param \app\components\instagram\models\Account $account
201
     * @param array|\app\components\instagram\models\Post[] $posts
202
     * @return array
203
     */
204
    private function postsStats(Account $account, array $posts): array
205
    {
206
        if (!empty($this->postStats)) {
207
            return $this->postStats;
208
        }
209
210
        $er = [];
211
        if ($account->followedBy) {
212
            foreach ($posts as $post) {
213
                $er[] = ($post->likes + $post->comments) / $account->followedBy;
214
            }
215
        }
216
        $er = $er ? array_sum($er) / count($er) : 0;
217
218
        $avgLikes = [];
219
        $avgComments = [];
220
        foreach ($posts as $post) {
221
            $avgLikes[] = $post->likes;
222
            $avgComments[] = $post->comments;
223
        }
224
        $avgLikes = $avgLikes ? array_sum($avgLikes) / count($avgLikes) : 0;
225
        $avgComments = $avgComments ? array_sum($avgComments) / count($avgComments) : 0;
226
227
        return $this->postStats = [
228
            'er' => $er,
229
            'avg_likes' => $avgLikes,
230
            'avg_comments' => $avgComments,
231
        ];
232
    }
233
}