Completed
Pull Request — master (#163)
by Corey
03:20
created

UserBehavior::calculateScore()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 22
c 0
b 0
f 0
rs 9.7998
cc 4
nc 5
nop 2

1 Method

Rating   Name   Duplication   Size   Complexity  
A UserBehavior::decorate() 0 10 4
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', 'behavior_id', 'date'], 'required'],
52
      [['user_id', 'behavior_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 getCheckinBreakdown(int $period = 30) {
129
    $datetimes = $this->time->getDateTimesInPeriod($period);
0 ignored issues
show
Bug introduced by
The method getDateTimesInPeriod() does not exist on common\interfaces\TimeInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to common\interfaces\TimeInterface. ( Ignorable by Annotation )

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

129
    /** @scrutinizer ignore-call */ 
130
    $datetimes = $this->time->getDateTimesInPeriod($period);
Loading history...
130
    $key = "scores_of_last_month_".Yii::$app->user->id."_{$period}_".$this->time->getLocalDate();
131
    $scores = Yii::$app->cache->get($key);
132
133
    if($scores === false) {
134
      $scores = [];
135
      foreach($datetimes as $datetime) {
136
        $behaviors = self::decorateWithCategory($this->getBehaviorsWithCounts($datetime));
137
        $scores[$datetime->format('Y-m-d')] = $this->getBehaviorsByCategory($behaviors);
138
      }
139
140
      Yii::$app->cache->set($key, $scores, 60*60*24);
141
    }
142
143
    return $scores;
144
  }
145
146
  /**
147
   * Returns a list of the most-selected behaviors
148
   * @param integer $limit the desired number of behaviors
149
   */
150
  public function getTopBehaviors(int $limit = 5) {
151
    return self::decorateWithCategory($this->getBehaviorsWithCounts($limit));
152
  }
153
154
  /**
155
   * Returns a list of categories and the number of selected behaviors in each category
156
   * @param array $decorated_behaviors an array of behaviors ran through self::decorateWithCategory()
157
   */
158
  public function getBehaviorsByCategory(array $decorated_behaviors) {
159
    $arr = array_reduce($decorated_behaviors, function($acc, $row) {
160
      $cat_id = $row['behavior']['category']['id'];
161
      if(array_key_exists($cat_id, $acc)) {
162
        $acc[$cat_id]['count'] += $row['count'];
163
      } else {
164
        $acc[$cat_id] = [
165
          'name'      => $row['behavior']['category']['name'],
166
          'count'     => $row['count'],
167
          'color'     => \common\models\Category::$colors[$cat_id]['color'],
168
          'highlight' => \common\models\Category::$colors[$cat_id]['highlight'],
169
        ];
170
      }
171
      return $acc;
172
    }, []);
173
    ksort($arr);
174
    return $arr;
175
  }
176
177
  public static function decorate(array $uo, $with_category = false) {
178
    foreach($uo as &$o) {
179
      if($behavior = \common\models\Behavior::getBehavior('id', $o['behavior_id'])) {
180
        $o['behavior'] = $behavior;
181
        if($with_category) {
182
          $o['behavior']['category'] = \common\models\Category::getCategory('id', $o['behavior']['category_id']);
183
        }
184
      }
185
    }
186
    return $uo;
187
  }
188
189
  public static function decorateWithCategory(array $uo) {
190
    return self::decorate($uo, true);
191
  }
192
193
  /*
194
   * Returns a list of the user's behaviors with a count of each behavior's
195
   * occurence, sorted in descending order of occurrence.
196
   * @param integer|\DateTime $limit a limit for the number of behaviors to return if an integer is supplied. If a \DateTime object is given, then it returns the 
197
   */
198
  public function getBehaviorsWithCounts($limit = null) {
199
    $query = new Query;
200
    $query->params = [":user_id" => Yii::$app->user->id];
201
    $query->select("user_id, behavior_id, COUNT(id) as count")
202
      ->from('user_behavior_link')
203
      ->groupBy('behavior_id, user_id')
204
      ->having('user_id = :user_id')
205
      ->orderBy('count DESC');
206
207
    if($limit instanceof \DateTime) {
208
      list($start, $end) = $this->time->getUTCBookends($limit->format('Y-m-d'));
209
      $query->params += [':start_date' => $start, ':end_date' => $end];
210
      $query->where('user_id=:user_id AND date > :start_date AND date <= :end_date');
211
    } else if(is_int($limit)) {
212
      $query->limit($limit);
213
    }
214
215
    return $query->all();
216
  }
217
}
218