Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.73% covered (success)
92.73%
51 / 55
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ProcessRolePlayUsageAsyncJob
92.73% covered (success)
92.73%
51 / 55
60.00% covered (warning)
60.00%
3 / 5
9.03
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 handle
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 backoff
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 upsertDailyBucket
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
1 / 1
2
 updateUserInfo
80.00% covered (warning)
80.00%
12 / 15
0.00% covered (danger)
0.00%
0 / 1
4.13
1<?php
2
3namespace App\Jobs;
4
5use App\Http\Models\Auth\User;
6use App\Http\Models\FlyMsgUserDailyUsage;
7use App\Http\Models\UserInfo;
8use App\Http\Models\UserRolePlayProgression;
9use App\Traits\ObjectMapper;
10use App\Traits\UsageTrait;
11use Carbon\Carbon;
12use Illuminate\Bus\Queueable;
13use Illuminate\Contracts\Queue\ShouldQueue;
14use Illuminate\Foundation\Bus\Dispatchable;
15use Illuminate\Queue\InteractsWithQueue;
16use Illuminate\Queue\SerializesModels;
17use MongoDB\BSON\UTCDateTime;
18
19/**
20 * Upserts the daily FlyMsgUserDailyUsage bucket and UserInfo aggregate
21 * counters for RolePlay metrics, mirroring the pattern used by
22 * ProcessFlyGrammarActionsAsyncJob.
23 *
24 * Accepts a user_id and the event date. All daily fields are recomputed
25 * from source collections via UsageTrait::getUsage() which counts soft-
26 * deleted roleplay rows via withTrashed() so daily historical metadata
27 * stays stable even when users delete sessions or personas.
28 */
29class ProcessRolePlayUsageAsyncJob implements ShouldQueue
30{
31    use Dispatchable, InteractsWithQueue, ObjectMapper, Queueable, SerializesModels, UsageTrait;
32
33    public $tries = 5;
34
35    public function __construct(
36        public string $userId,
37        public Carbon $eventDate,
38    ) {}
39
40    public function handle(): void
41    {
42        $data = $this->getUsage($this->userId, $this->eventDate);
43
44        $this->upsertDailyBucket($data);
45        $this->updateUserInfo();
46    }
47
48    public function backoff(): array
49    {
50        return [10, 30, 60, 110, 300];
51    }
52
53    private function upsertDailyBucket(array $data): void
54    {
55        $year = $this->eventDate->year;
56        $month = $this->eventDate->month;
57        $day = $this->eventDate->day;
58
59        $findByDate = FlyMsgUserDailyUsage::where('user_id', $this->userId)
60            ->where('year', $year)
61            ->where('month', $month)
62            ->where('day', $day)
63            ->first();
64
65        if ($findByDate) {
66            $findByDate->update($data);
67
68            return;
69        }
70
71        FlyMsgUserDailyUsage::GenerateNewRecord(
72            $year,
73            $month,
74            $day,
75            $this->userId,
76            $data['time_saved'],
77            $data['cost_savings'],
78            $data['characters_typed'],
79            $data['flycuts_created'],
80            $data['flyplates_added'],
81            $data['flyengage_count'],
82            $data['sentence_rewrite_count'],
83            $data['paragraph_rewrite_count'],
84            $data['flypost_count'],
85            $data['flycut_count'],
86            $data['fly_grammar_actions'] ?? 0,
87            $data['fly_grammar_accepted'] ?? 0,
88            $data['fly_grammar_autocorrect'] ?? 0,
89            $data['fly_grammar_autocomplete'] ?? 0,
90            $data['roleplay_personas_created'] ?? 0,
91            $data['roleplay_sessions_practiced'] ?? 0,
92            $data['roleplay_time_practiced'] ?? 0,
93            (float) ($data['roleplay_daily_avg_score'] ?? 0.0),
94        );
95    }
96
97    private function updateUserInfo(): void
98    {
99        $user = User::find($this->userId);
100        if (! $user) {
101            return;
102        }
103
104        $userInfo = UserInfo::where('email', $user->email)->first();
105        if (! $userInfo) {
106            return;
107        }
108
109        $userInfo->last_date_user_used_roleplay = new UTCDateTime($this->eventDate->timestamp * 1000);
110
111        $latestOverall = UserRolePlayProgression::where('user_id', $this->userId)
112            ->get()
113            ->map(fn ($p) => data_get($p->current_averages, 'overall'))
114            ->filter(fn ($v) => $v !== null)
115            ->avg();
116
117        if ($latestOverall !== null) {
118            $userInfo->last_roleplay_progression_score = round((float) $latestOverall, 4);
119        }
120
121        $userInfo->save();
122    }
123}