Completed
Pull Request — master (#163)
by Corey
02:33
created

UserBehavior::calculateScoreByUTCRange()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
namespace common\models;
4
5
use Yii;
6
use \common\interfaces\TimeInterface;
7
use \common\interfaces\BehaviorInterface;
8
use \common\interfaces\UserBehaviorInterface;
9
use \common\components\ActiveRecord;
10
use yii\db\Query;
11
use yii\helpers\ArrayHelper as AH;
12
use \DateTime;
13
use \DateTimeZone;
14
use yii\db\Expression;
15
16
/**
17
 * This is the model class for table "user_behavior_link".
18
 *
19
 * @property integer $id
20
 * @property integer $user_id
21
 * @property integer $behavior_id
22
 * @property string $date
23
 *
24
 * @property Behavior $user
25
 */
26
class UserBehavior extends ActiveRecord implements UserBehaviorInterface
27
{
28
  private $time;
29
  private $behavior;
30
31
  public function __construct(BehaviorInterface $behavior, TimeInterface $time, $config = []) {
32
    $this->behavior = $behavior;
33
    $this->time = $time;
34
    parent::__construct($config);
35
  }
36
37
  /**
38
   * @inheritdoc
39
   */
40
  public static function tableName()
41
  {
42
    return 'user_behavior_link';
43
  }
44
45
  /**
46
   * @inheritdoc
47
   */
48
  public function rules()
49
  {
50
    return [
51
      [['user_id', 'behaviorr_id', 'date'], 'required'],
52
      [['user_id', 'behaviorr_id'], 'integer'],
53
      //[['date'], 'string']
54
    ];
55
  }
56
57
  /**
58
   * @inheritdoc
59
   */
60
  public function attributeLabels()
61
  {
62
    return [
63
      'id'        => 'ID',
64
      'date'      => 'Date',
65
      'user_id'   => 'User ID',
66
      'behavior_id' => 'Behavior ID',
67
    ];
68
  }
69
70
  /**
71
   * @return \yii\db\ActiveQuery
72
   */
73
  public function getUser()
74
  {
75
    return $this->hasOne(\common\models\User::class, ['id' => 'user_id']);
76
  }
77
78
  public function getPastCheckinDates() {
79
    $past_checkin_dates = [];
80
    $query = new Query;
81
    $query->params = [":user_id" => Yii::$app->user->id];
82
    $query->select("date")
83
      ->from('user_behavior_link l')
84
      ->groupBy('date, user_id')
85
      ->having('user_id = :user_id');
86
    $temp_dates = $query->all();
87
    foreach($temp_dates as $temp_date) {
88
      $past_checkin_dates[] = $this->time->convertUTCToLocal($temp_date['date']);
89
    }
90
91
    return $past_checkin_dates;
92
  }
93
94
  public function getUserBehaviorsWithCategory($checkin_date) {
95
    list($start, $end) = $this->time->getUTCBookends($checkin_date);
96
97
    $query = new Query;
98
    $query->select('*')
99
      ->from('user_behavior_link')
100
      ->orderBy('behavior_id')
101
      ->where("user_id=:user_id
102
          AND date > :start_date
103
          AND date < :end_date",
104
      [
105
        ":user_id" => Yii::$app->user->id, 
106
        ":start_date" => $start, 
107
        ":end_date" => $end
108
      ]);
109
110
    $user_behaviors = self::decorateWithCategory($query->all());
111
    return AH::map($user_behaviors, 'id', function($a) { return $a['behavior']['name']; }, function($b) {return $b['behavior']['category_id']; });
112
  }
113
114
  public function getBehaviorsByDate($start, $end) {
115
      $uo = $this->find()
116
        ->select(['id', 'user_id', 'behavior_id', 'date'])
117
        ->where(
118
          "user_id=:user_id AND date > :start_date AND date <= :end_date",
119
          ["user_id" => Yii::$app->user->id, ':start_date' => $start, ":end_date" => $end]
120
        )
121
        ->orderBy('date')
122
        ->asArray()
123
        ->all();
124
125
      return self::decorate($uo, false);
126
  }
127
128
  public function calculateScoresOfLastMonth() {
129
    $key = "scores_of_last_month_".Yii::$app->user->id."_".$this->time->getLocalDate();
130
    $scores = Yii::$app->cache->get($key);
131
    if($scores === false) {
132
      $scores = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $scores is dead and can be removed.
Loading history...
133
134
      $dt = new DateTime("now", new DateTimeZone("UTC"));
135
      $dt2 = new DateTime("now", new DateTimeZone("UTC"));
136
      $end = $dt->add(new \DateInterval('PT2M')); // to be sure we have everything
137
      $start = $dt2->sub(new \DateInterval('P1M'));
138
      $periods = new \DatePeriod($start, new \DateInterval('P1D'), $end);
139
      $local_tz = new \DateTimeZone($this->time->timezone);
0 ignored issues
show
Bug introduced by
Accessing timezone on the interface common\interfaces\TimeInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
140
      $scores = [];
141
      foreach($periods as $period) {
142
        $period->setTimezone($local_tz);
143
        $scores[$period->format('Y-m-d')] = $this->getBehaviorsByCategory($period);
144
      }
145
146
      Yii::$app->cache->set($key, $scores, 60*60*24);
147
    }
148
149
    return $scores;
150
  }
151
152
  /**
153
   * Returns a list of the most-selected behaviors
154
   * @param integer $limit the desired number of behaviors
155
   */
156
  public function getTopBehaviors(int $limit = 5) {
157
    return self::decorateWithCategory($this->getBehaviorsWithCounts($limit));
158
  }
159
160
  /**
161
   * Returns a list of categories and the number of selected behaviors in each category
162
   */
163
  public function getBehaviorsByCategory(\DateTime $date = null) {
164
    $behaviors = self::decorateWithCategory($this->getBehaviorsWithCounts($date));
165
166
    $arr = array_reduce($behaviors, function($acc, $row) {
167
      $cat_id = $row['behavior']['category']['id'];
168
      if(array_key_exists($cat_id, $acc)) {
169
        $acc[$cat_id]['count'] += $row['count'];
170
      } else {
171
        $acc[$cat_id] = [
172
          'name'      => $row['behavior']['category']['name'],
173
          'count'     => $row['count'],
174
          'color'     => \common\models\Category::$colors[$cat_id]['color'],
175
          'highlight' => \common\models\Category::$colors[$cat_id]['highlight'],
176
        ];
177
      }
178
      return $acc;
179
    }, []);
180
    ksort($arr);
181
    return $arr;
182
  }
183
184
  public static function decorate(array $uo, $with_category = false) {
185
    foreach($uo as &$o) {
186
      if($behavior = \common\models\Behavior::getBehavior('id', $o['behavior_id'])) {
187
        $o['behavior'] = $behavior;
188
        if($with_category) {
189
          $o['behavior']['category'] = \common\models\Category::getCategory('id', $o['behavior']['category_id']);
190
        }
191
      }
192
    }
193
    return $uo;
194
  }
195
196
  public static function decorateWithCategory(array $uo) {
197
    return self::decorate($uo, true);
198
  }
199
200
  public function getBehaviorsWithCounts($limit = null) {
201
    $query = new Query;
202
    $query->params = [":user_id" => Yii::$app->user->id];
203
    $query->select("user_id, behavior_id, COUNT(id) as count")
204
      ->from('user_behavior_link')
205
      ->groupBy('behavior_id, user_id')
206
      ->having('user_id = :user_id')
207
      ->orderBy('count DESC');
208
209
    if($limit instanceof \DateTime) {
210
      list($start, $end) = $this->time->getUTCBookends($limit->format('Y-m-d'));
211
      $query->params += [':start_date' => $start, ':end_date' => $end];
212
      $query->where('user_id=:user_id AND date > :start_date AND date <= :end_date');
213
    } else if(is_int($limit)) {
214
      $query->limit($limit);
215
    }
216
217
    return $query->all();
218
  }
219
}
220