Issues (76)

components/builders/AccountBuilder.php (2 issues)

1
<?php
2
/**
3
 * Created for IG Monitoring.
4
 * User: jakim <[email protected]>
5
 * Date: 12.06.2018
6
 */
7
8
namespace app\components\builders;
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 AccountBuilder extends Component
24
{
25
    use SaveModelTrait, SetAccountTrait, NextUpdateCalculatorTrait;
0 ignored issues
show
The trait app\components\traits\SaveModelTrait requires the property $errors which is not provided by app\components\builders\AccountBuilder.
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()
68
    {
69
        $this->account->monitoring = 1;
70
71
        return $this;
72
    }
73
74
    public function setIsValid()
75
    {
76
        $this->account->is_valid = 1;
77
        $this->account->invalidation_count = 0;
78
        $this->account->invalidation_type_id = null;
79
80
        return $this;
81
    }
82
83
    public function setIsInvalid(?int $invalidationType = null)
84
    {
85
        $this->account->is_valid = 0;
86
        $this->account->invalidation_count = (int)$this->account->invalidation_count + 1;
87
        $this->account->invalidation_type_id = $invalidationType;
88
89
        return $this;
90
    }
91
92
    /**
93
     * If true, then will be automatically calculate from invalidation_count
94
     *
95
     * @param true|int|null $interval
96
     * @return $this
97
     */
98
    public function setNextStatsUpdate($interval = 24)
99
    {
100
        $this->account->update_stats_after = $this->getNextUpdateDate($this->account, $interval);
101
102
        return $this;
103
    }
104
105
    /**
106
     * Direct account statistics.
107
     *
108
     * @param \app\components\instagram\models\Account $account
109
     * @param array|\app\components\instagram\models\Post[] $posts
110
     * @param bool $createHistory
111
     * @return $this
112
     */
113
    public function setStats(Account $account, array $posts, bool $createHistory = true)
114
    {
115
        $this->account->touch('stats_updated_at');
0 ignored issues
show
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

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