Passed
Push — master ( 9f850d...5c55bf )
by Paweł
03:12
created

AccountUpdater::createHistory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 0
dl 0
loc 14
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created for IG Monitoring.
4
 * User: jakim <[email protected]>
5
 * Date: 12.06.2018
6
 */
7
8
namespace app\components;
9
10
11
use app\components\instagram\models\Account;
12
use app\components\traits\FindOrCreate;
13
use app\models\AccountStats;
14
use yii\base\Component;
15
use yii\base\InvalidConfigException;
16
use yii\db\ActiveRecord;
17
use yii\web\ServerErrorHttpException;
18
19
class AccountUpdater extends Component
20
{
21
    use FindOrCreate;
22
23
    /**
24
     * @var \app\models\Account
25
     */
26
    public $account;
27
28
    private $postStats;
29
30
    public function init()
31
    {
32
        parent::init();
33
        if (!$this->account instanceof \app\models\Account) {
0 ignored issues
show
introduced by
$this->account is always a sub-type of app\models\Account. If $this->account can have other possible types, add them to components/AccountUpdater.php:24.
Loading history...
34
            throw new InvalidConfigException('Property \'account\' must be set and be type od \'\app\models\Account\'.');
35
        }
36
    }
37
38
    public function details(Account $account): \app\models\Account
39
    {
40
        $this->account->username = $account->username;
41
        $this->account->instagram_id = (string)$account->id;
42
        $this->account->profile_pic_url = $account->profilePicUrl;
43
        $this->account->full_name = $account->fullName;
44
        $this->account->biography = $account->biography;
45
        $this->account->external_url = $account->externalUrl;
46
47
        $this->saveModel($this->account);
48
49
        return $this->account;
50
    }
51
52
    /**
53
     * Direct account statistics.
54
     *
55
     * @param \app\components\instagram\models\Account $account
56
     * @param array $posts
57
     * @return \app\models\AccountStats|null
58
     * @throws \yii\web\ServerErrorHttpException
59
     */
60
    public function stats(Account $account, array $posts): ?AccountStats
61
    {
62
        $accountStats = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $accountStats is dead and can be removed.
Loading history...
63
        $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

63
        $this->account->/** @scrutinizer ignore-call */ 
64
                        touch('stats_updated_at');
Loading history...
64
65
        if ($this->account->media === null || $this->statsNeedUpdate($account, $posts)) {
66
            $postsStats = $this->postsStats($account, $posts);
67
            $this->account->media = $account->media;
68
            $this->account->follows = $account->follows;
69
            $this->account->followed_by = $account->followedBy;
70
            $this->account->er = $postsStats['er'];
71
            $this->account->avg_likes = $postsStats['avg_likes'];
72
            $this->account->avg_comments = $postsStats['avg_comments'];
73
74
            $accountStats = $this->createHistory();
75
76
            return $accountStats;
77
        }
78
        $this->saveModel($this->account);
79
    }
80
81
    protected function createHistory()
82
    {
83
        $accountStats = new AccountStats([
84
            'account_id' => $this->account->id,
85
            'media' => $this->account->media,
86
            'follows' => $this->account->follows,
87
            'followed_by' => $this->account->followed_by,
88
            'er' => $this->account->er,
89
            'avg_likes' => $this->account->avg_likes,
90
            'avg_comments' => $this->account->avg_comments,
91
        ]);
92
        $this->saveModel($accountStats);
93
94
        return $accountStats;
95
    }
96
97
    private function statsNeedUpdate(Account $account, array $posts): bool
98
    {
99
        $res = $this->account->media != $account->media ||
100
            $this->account->follows != $account->follows ||
101
            $this->account->followed_by != $account->followedBy;
102
        if ($res === false) {
103
            $postStats = $this->postsStats($account, $posts);
104
            $res = $this->account->er != $postStats['er'] ||
105
                $this->account->avg_likes != $postStats['avg_likes'] ||
106
                $this->account->avg_comments != $postStats['avg_comments'];
107
        }
108
109
        return $res;
110
    }
111
112
    /**
113
     * Statistics calculated from posts (engagement, avg_likes, avg_comments).
114
     *
115
     * @param \app\components\instagram\models\Account $account
116
     * @param array $posts
117
     * @return array
118
     */
119
    private function postsStats(Account $account, array $posts): array
120
    {
121
        if (!empty($this->postStats)) {
122
            return $this->postStats;
123
        }
124
125
        $er = [];
126
        if ($account->followedBy) {
127
            foreach ($posts as $post) {
128
                $er[] = ($post->likes + $post->comments) / $account->followedBy;
129
            }
130
        }
131
        $er = $er ? array_sum($er) / \count($er) : 0;
132
133
        $avgLikes = [];
134
        $avgComments = [];
135
        foreach ($posts as $post) {
136
            $avgLikes[] = $post->likes;
137
            $avgComments[] = $post->comments;
138
        }
139
        $avgLikes = $avgLikes ? array_sum($avgLikes) / \count($avgLikes) : 0;
140
        $avgComments = $avgComments ? array_sum($avgComments) / \count($avgComments) : 0;
141
142
        return $this->postStats = [
143
            'er' => $er,
144
            'avg_likes' => $avgLikes,
145
            'avg_comments' => $avgComments,
146
        ];
147
    }
148
149
    private function saveModel(ActiveRecord $model)
150
    {
151
        if (!$model->save()) {
152
            throw new ServerErrorHttpException(sprintf('Validation \'%s\': %s', get_class($model), json_encode($model->errors)));
153
        }
154
155
        return true;
156
    }
157
}