Passed
Push — master ( 0db423...f9fe2f )
by Paweł
03:15
created

AccountManager::internalUpdateMedia()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 3
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created for IG Monitoring.
4
 * User: jakim <[email protected]>
5
 * Date: 11.01.2018
6
 */
7
8
namespace app\components;
9
10
11
use app\components\http\Client;
12
use app\components\http\CacheStorage;
13
use app\models\Account;
14
use app\models\AccountStats;
15
use app\models\AccountTag;
16
use app\models\Media;
17
use app\models\Proxy;
18
use app\models\Tag;
19
use GuzzleHttp\HandlerStack;
20
use Jakim\Query\AccountQuery;
21
use Kevinrob\GuzzleCache\CacheMiddleware;
22
use Kevinrob\GuzzleCache\Strategy\GreedyCacheStrategy;
23
use yii\base\Component;
24
use yii\base\InvalidConfigException;
25
26
class AccountManager extends Component
27
{
28
    /**
29
     * @var Proxy
30
     */
31
    public $proxy;
32
33
    public function fetchDetails(Account $account): \Jakim\Model\Account
34
    {
35
        $query = $this->queryFactory($account);
36
37
        return $query->findOne($account->username);
38
    }
39
40
    /**
41
     * Fetch data from API, update details and stats.
42
     *
43
     * @param \app\models\Account $account
44
     * @return \app\models\Account
45
     */
46
    public function update(Account $account)
47
    {
48
        $data = $this->fetchDetails($account);
49
        $this->updateDetails($account, $data);
50
        $this->updateStats($account, $data);
51
52
        return $account;
53
    }
54
55
    public function updateDetails(Account $account, \Jakim\Model\Account $data = null): Account
56
    {
57
        $data = $data ?: $this->fetchDetails($account);
58
59
        $account->profile_pic_url = $data->profilePicUrl;
60
        $account->full_name = $data->fullName;
61
        $account->biography = $data->biography;
62
        $account->external_url = $data->externalUrl;
63
        $account->instagram_id = $data->id;
64
65
        $this->updateProfilePic($account);
66
67
        $account->save();
68
69
        return $account;
70
    }
71
72
    public function updateStats(Account $account, \Jakim\Model\Account $data = null): ?AccountStats
73
    {
74
        $data = $data ?: $this->fetchDetails($account);
75
        $accountStats = null;
76
77
        if ($this->statsNeedUpdate($account, $data)) {
78
            $accountStats = new AccountStats();
79
            $accountStats->followed_by = $data->followedBy;
80
            $accountStats->follows = $data->follows;
81
            $accountStats->media = $data->media;
82
            $account->link('accountStats', $accountStats);
83
            $account->resetStatsCache();
84
        }
85
        $this->updateMedia($account);
86
        $this->updateEr($account);
87
88
        return $accountStats;
89
    }
90
91
    protected function updateEr(Account $account, int $mediaLimit = 10)
92
    {
93
        if (!$account->lastAccountStats) {
94
            return false;
95
        }
96
97
        $media = Media::find()
98
            ->innerJoinWith('mediaStats')
99
            ->andWhere(['account_id' => $account->id])
100
            ->limit($mediaLimit)
101
            ->groupBy('media.id')
102
            ->all();
103
104
        $er = [];
105
        foreach ($media as $m) {
106
            $er[] = ($m->lastMediaStats->likes + $m->lastMediaStats->comments) / $m->lastMediaStats->account_followed_by;
107
        }
108
109
        $er = $er ? array_sum($er) / count($er) : 0;
110
        $account->lastAccountStats->er = round($er, 4);
111
112
        return $account->lastAccountStats->update();
113
    }
114
115
    public function updateMedia(Account $account, int $limit = 10)
116
    {
117
        $manager = \Yii::createObject([
118
            'class' => MediaManager::class,
119
            'account' => $account,
120
        ]);
121
122
        $query = $this->queryFactory($account);
123
        $items = $query->findPosts($account->username, $limit);
124
125
        foreach ($items as $item) {
126
            $media = Media::findOne(['instagram_id' => $item->id]);
127
            if ($media === null) {
128
                $media = new Media(['account_id' => $account->id]);
129
            }
130
            $manager->update($media, $item);
131
        }
132
    }
133
134
    public function updateTags(Account $account, array $tags)
135
    {
136
        // clearing
137
        AccountTag::deleteAll(['account_id' => $account->id]);
138
139
        // add
140
        foreach (array_filter($tags) as $tag) {
141
            $model = Tag::findOne(['name' => $tag]);
142
            if ($model === null && $tag) {
143
                $model = new Tag(['name' => $tag]);
144
                $model->insert();
145
            }
146
            $account->link('tags', $model);
147
        }
148
    }
149
150
    public function saveUsernames(array $usernames)
151
    {
152
        $createdAt = (new \DateTime())->format('Y-m-d H:i:s');
153
        $rows = array_map(function ($username) use ($createdAt) {
154
            return [
155
                $username,
156
                $createdAt,
157
                $createdAt,
158
            ];
159
        }, $usernames);
160
161
        $sql = \Yii::$app->db->getQueryBuilder()
162
            ->batchInsert(Account::tableName(), ['username', 'updated_at', 'created_at'], $rows);
163
        $sql = str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $sql);
164
        \Yii::$app->db->createCommand($sql)
165
            ->execute();
166
    }
167
168
    protected function getProxy(Account $account): Proxy
169
    {
170
        $proxy = $this->proxy ?: $account->proxy;
171
        if (!$proxy || !$proxy->active) {
172
            throw new InvalidConfigException('Account proxy must be set and be active.');
173
        }
174
175
        return $proxy;
176
    }
177
178
    protected function updateProfilePic(Account $account): void
179
    {
180
        if ($account->profile_pic_url) {
181
            $url = $account->profile_pic_url;
182
            $account->profile_pic_url = null;
183
184
            $filename = sprintf('%s_%s', $account->username, basename($url));
185
            $path = \Yii::getAlias("@app/web/uploads/{$filename}");
186
            $fileExists = file_exists($path);
187
188
            if (!$fileExists) {
189
                $proxy = $this->getProxy($account);
190
                $client = Client::factory($proxy);
191
                $content = $client->get($url)->getBody()->getContents();
192
                if ($content && file_put_contents($path, $content)) {
193
                    $account->profile_pic_url = "/uploads/{$filename}";
194
                }
195
            } elseif ($fileExists) {
0 ignored issues
show
introduced by
The condition $fileExists is always true.
Loading history...
196
                $account->profile_pic_url = "/uploads/{$filename}";
197
            }
198
        }
199
    }
200
201
    private function statsNeedUpdate(Account $account, \Jakim\Model\Account $data): bool
202
    {
203
        if (!$account->lastAccountStats) {
204
            return true;
205
        }
206
207
        return $account->lastAccountStats->followed_by != $data->followedBy ||
208
            $account->lastAccountStats->follows != $data->follows ||
209
            $account->lastAccountStats->media != $data->media;
210
    }
211
212
    /**
213
     * @param \app\models\Account $account
214
     * @return AccountQuery
215
     * @throws \yii\base\InvalidConfigException
216
     */
217
    private function queryFactory(Account $account): AccountQuery
218
    {
219
        $proxy = $this->getProxy($account);
220
221
//        $stack = HandlerStack::create();
222
//        $stack->push(new CacheMiddleware(
223
//            new GreedyCacheStrategy(
224
//                new CacheStorage(), 3600)
225
//        ), 'cache');
226
//        $client = Client::factory($proxy, ['handler' => $stack]);
227
        $client = Client::factory($proxy);
228
229
        $query = new AccountQuery($client);
230
231
        return $query;
232
    }
233
}