|
41573
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41573
|
|
41574
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41574
|
|
41596
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41596
|
|
41597
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41597
|
|
41601
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41601
|
|
41620
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41620
|
|
41621
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41621
|
|
41623
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
41623
|
|
54877
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
namespace Jiminny\Console\Commands\Activities;
use Illuminate\Console\Command;
use Illuminate\Contracts\Events\Dispatcher;
use Jiminny\Contracts\ES\Events\UpdateSingleEntity;
use Jiminny\Contracts\ES\UpdateTargetEnum;
use Jiminny\Models\Activity;
class UpdateActivityElasticSearchDocumentCommand extends Command
{
protected $signature = 'activity:update:es {activityId}';
protected $description = 'Update ES document synchronously';
public function __construct(private readonly Dispatcher $eventDispatcher)
{
parent::__construct();
}
public function handle(): void
{
$activityId = $this->argument('activityId');
/** @var Activity $activity */
$activity = Activity::idOrUuId($activityId)->first();
$this->info('Sending activity for ES update...');
$this->eventDispatcher->dispatch(
new UpdateSingleEntity(
entityId: $activity->getId(),
updateTarget: UpdateTargetEnum::ACTIVITY,
purpose: 'cli-command-activity-update-es',
)
);
$this->info('Done.');
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
EventSubscriber, folder
FilterDefinition, folder
DealInsights, folder
Security
TeamInsights
ActivityActualDate.php, class
ActivityChannel.php, final class
ActivityDurationRange.php, class
ActivityFilter.php, final class
ActivityPlaylistIn.php, final class
ActivityProviderIn.php, final class
ActivityRecorded.php, class
ActivityRecordingStopped.php, final class
ActivityScheduledDate.php, final class
ActivityStatusIn.php, class
ActivityType.php, final class
ActivityUpdatedDate.php, final class
AiCallScoreFilter.php, final class
AutoScoreFilter.php, final class
ClosedDealsFilter.php, final class
CoachingFeedbackAverageScore.php, final class
CoachingFeedbackCoachUserIn.php, final class
CommentCountRange.php, final class
CrmFieldCollection.php, final class
CurrentStage.php, final class
Customer.php, final class
CustomerMonologueDuration.php, final class
CustomerQuestionCount.php, final class
DealAge.php, final class
DealCloseDate.php, final class
DealValue.php, final class
EngagingQuestionCount.php, final class
ExternalId.php, final class
HasPendingAiCrmNotes.php, final class
HasTopicTriggersFilterDefinition.php, final class
HasTranscription.php, final class
InputTypeEnum.php, final class
InsightfulQuestionCount.php, final class
LanguageFilterDefinition.php, final class
LoggedToCrm.php, final class
NudgeRunId.php, final class
OnlyActiveUsers.php, final class
OrganiserGroupIn.php, class
OrganiserTeamIn.php, final class
OrganiserUserIn.php, class
OrganiserUserNotIn.php, final class
ParticipantUserIn.php, final class
PartnerFilterDefinition.php, final class
PatienceRange.php, final class
PlaybackTopicFilterDefinition.php, final class
ProviderFilterDefinition.php, final class
ShowInternalExternalActivitiesFilter.php, final class
SortBy.php, final class
SpeechRate.php, final class
StageAtCallFilterDefinition.php, class
TalkTimeRatio.php, final class
TeamMemberUserIn.php, final class
TranscriptionComposite.php, final class
UserGroupInOptionalFilter.php, final class
UserMonologueDuration.php, final class
UserQuestionCount.php, final class
Service
AbstractStageFilterDefinition.php, abstract class
ActivitySearchServiceProvider.php, final class
DealInsightsPeriodFilterFactory.php
DealInsightsPeriodFilterFactoryInterface.php, interface
FilterDefinition.php, abstract class
FilterDefinitionCollection.php, class
FilterDefinitionQuery.php, class
FilterDefinitionQueryCollection.php, class...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
54877
|
|
54878
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
namespace Jiminny\Console\Commands\Activities;
use Illuminate\Console\Command;
use Illuminate\Contracts\Events\Dispatcher;
use Jiminny\Contracts\ES\Events\UpdateSingleEntity;
use Jiminny\Contracts\ES\UpdateTargetEnum;
use Jiminny\Models\Activity;
class UpdateActivityElasticSearchDocumentCommand extends Command
{
protected $signature = 'activity:update:es {activityId}';
protected $description = 'Update ES document synchronously';
public function __construct(private readonly Dispatcher $eventDispatcher)
{
parent::__construct();
}
public function handle(): void
{
$activityId = $this->argument('activityId');
/** @var Activity $activity */
$activity = Activity::idOrUuId($activityId)->first();
$this->info('Sending activity for ES update...');
$this->eventDispatcher->dispatch(
new UpdateSingleEntity(
entityId: $activity->getId(),
updateTarget: UpdateTargetEnum::ACTIVITY,
purpose: 'cli-command-activity-update-es',
)
);
$this->info('Done.');
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
EventSubscriber, folder
FilterDefinition, folder
DealInsights, folder
Security
TeamInsights
ActivityActualDate.php, class
ActivityChannel.php, final class
ActivityDurationRange.php, class
ActivityFilter.php, final class
ActivityPlaylistIn.php, final class
ActivityProviderIn.php, final class
ActivityRecorded.php, class
ActivityRecordingStopped.php, final class
ActivityScheduledDate.php, final class
ActivityStatusIn.php, class
ActivityType.php, final class
ActivityUpdatedDate.php, final class
AiCallScoreFilter.php, final class
AutoScoreFilter.php, final class
ClosedDealsFilter.php, final class
CoachingFeedbackAverageScore.php, final class
CoachingFeedbackCoachUserIn.php, final class
CommentCountRange.php, final class
CrmFieldCollection.php, final class
CurrentStage.php, final class
Customer.php, final class
CustomerMonologueDuration.php, final class
CustomerQuestionCount.php, final class
DealAge.php, final class
DealCloseDate.php, final class
DealValue.php, final class
EngagingQuestionCount.php, final class
ExternalId.php, final class
HasPendingAiCrmNotes.php, final class
HasTopicTriggersFilterDefinition.php, final class
HasTranscription.php, final class
InputTypeEnum.php, final class
InsightfulQuestionCount.php, final class
LanguageFilterDefinition.php, final class
LoggedToCrm.php, final class
NudgeRunId.php, final class
OnlyActiveUsers.php, final class
OrganiserGroupIn.php, class
OrganiserTeamIn.php, final class
OrganiserUserIn.php, class
OrganiserUserNotIn.php, final class
ParticipantUserIn.php, final class
PartnerFilterDefinition.php, final class
PatienceRange.php, final class
PlaybackTopicFilterDefinition.php, final class
ProviderFilterDefinition.php, final class
ShowInternalExternalActivitiesFilter.php, final class
SortBy.php, final class
SpeechRate.php, final class
StageAtCallFilterDefinition.php, class
TalkTimeRatio.php, final class
TeamMemberUserIn.php, final class
TranscriptionComposite.php, final class
UserGroupInOptionalFilter.php, final class
UserMonologueDuration.php, final class
UserQuestionCount.php, final class
Service
AbstractStageFilterDefinition.php, abstract class
ActivitySearchServiceProvider.php, final class
DealInsightsPeriodFilterFactory.php
DealInsightsPeriodFilterFactoryInterface.php, interface
FilterDefinition.php, abstract class
FilterDefinitionCollection.php, class
FilterDefinitionQuery.php, class
FilterDefinitionQueryCollection.php, class
FilteredValueContainerInterface.php, interface
IntMinMaxRange.php, class
AiActivityType
AiAutomation
AiCallScoring
AskAnything
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
54878
|
|
54879
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
namespace Jiminny\Console\Commands\Activities;
use Illuminate\Console\Command;
use Illuminate\Contracts\Events\Dispatcher;
use Jiminny\Contracts\ES\Events\UpdateSingleEntity;
use Jiminny\Contracts\ES\UpdateTargetEnum;
use Jiminny\Models\Activity;
class UpdateActivityElasticSearchDocumentCommand extends Command
{
protected $signature = 'activity:update:es {activityId}';
protected $description = 'Update ES document synchronously';
public function __construct(private readonly Dispatcher $eventDispatcher)
{
parent::__construct();
}
public function handle(): void
{
$activityId = $this->argument('activityId');
/** @var Activity $activity */
$activity = Activity::idOrUuId($activityId)->first();
$this->info('Sending activity for ES update...');
$this->eventDispatcher->dispatch(
new UpdateSingleEntity(
entityId: $activity->getId(),
updateTarget: UpdateTargetEnum::ACTIVITY,
purpose: 'cli-command-activity-update-es',
)
);
$this->info('Done.');
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
EventSubscriber, folder
FilterDefinition, folder
DealInsights, folder
Security
TeamInsights
ActivityActualDate.php, class
ActivityChannel.php, final class
ActivityDurationRange.php, class
ActivityFilter.php, final class
ActivityPlaylistIn.php, final class
ActivityProviderIn.php, final class
ActivityRecorded.php, class
ActivityRecordingStopped.php, final class
ActivityScheduledDate.php, final class
ActivityStatusIn.php, class
ActivityType.php, final class
ActivityUpdatedDate.php, final class
AiCallScoreFilter.php, final class
AutoScoreFilter.php, final class
ClosedDealsFilter.php, final class
CoachingFeedbackAverageScore.php, final class
CoachingFeedbackCoachUserIn.php, final class
CommentCountRange.php, final class
CrmFieldCollection.php, final class
CurrentStage.php, final class
Customer.php, final class
CustomerMonologueDuration.php, final class
CustomerQuestionCount.php, final class
DealAge.php, final class
DealCloseDate.php, final class
DealValue.php, final class
EngagingQuestionCount.php, final class
ExternalId.php, final class
HasPendingAiCrmNotes.php, final class
HasTopicTriggersFilterDefinition.php, final class
HasTranscription.php, final class
InputTypeEnum.php, final class
InsightfulQuestionCount.php, final class
LanguageFilterDefinition.php, final class
LoggedToCrm.php, final class
NudgeRunId.php, final class
OnlyActiveUsers.php, final class
OrganiserGroupIn.php, class
OrganiserTeamIn.php, final class
OrganiserUserIn.php, class
OrganiserUserNotIn.php, final class
ParticipantUserIn.php, final class
PartnerFilterDefinition.php, final class
PatienceRange.php, final class
PlaybackTopicFilterDefinition.php, final class
ProviderFilterDefinition.php, final class
ShowInternalExternalActivitiesFilter.php, final class
SortBy.php, final class
SpeechRate.php, final class
StageAtCallFilterDefinition.php, class
TalkTimeRatio.php, final class
TeamMemberUserIn.php, final class
TranscriptionComposite.php, final class
UserGroupInOptionalFilter.php, final class
UserMonologueDuration.php, final class
UserQuestionCount.php, final class
Service
AbstractStageFilterDefinition.php, abstract class
ActivitySearchServiceProvider.php, final class
DealInsightsPeriodFilterFactory.php
DealInsightsPeriodFilterFactoryInterface.php, interface
FilterDefinition.php, abstract class
FilterDefinitionCollection.php, class
FilterDefinitionQuery.php, class
FilterDefinitionQueryCollection.php, class
FilteredValueContainerInterface.php, interface
IntMinMaxRange.php, class
AiActivityType
AiAutomation
AiCallScoring
AskAnything
AskJiminnyAi
AWS
BillingManagement...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
54879
|
|
54929
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\API\V2;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
class AskJiminnyReportsController extends Controller
{
public function __construct(
private readonly AutomatedReportsService $automatedReportsService,
private readonly LoggerInterface $logger,
) {
}
private function isNotOwnedByUser(AutomatedReport $report, User $user): bool
{
return $report->getTeamId() !== $user->getTeamId()
|| $report->getAttribute('created_by') !== $user->getId();
}
public function getFormData(Request $request, ?string $uuid = null): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $uuid ? $this->automatedReportsService->getReport($uuid) : null;
return new JsonResponse(
$this->automatedReportsService->getAskJiminnyReportFormData($user, $report)
);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report form data', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch form data'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Create a new Ask Jiminny report.
*/
public function create(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$data = $this->automatedReportsService->createAskJiminnyReport($request->all(), $user);
return new JsonResponse($data);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to create Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to create report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Update an existing Ask Jiminny report.
*/
public function update(Request $request, string $uuid): JsonResponse
{
\Illuminate\Support\Facades\Log::channel('custom_channel')->info("UPDATE");
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReport($report, $request->all(), $user);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to update Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to update report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Toggle Ask Jiminny report status (enable/disable).
*/
public function toggleStatus(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReportStatus(
$report,
(bool) $request->input('enabled'),
);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to toggle Ask Jiminny report status', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to toggle report status'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* List all Ask Jiminny reports.
*/
public function list(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$sortColumn = $request->input('sort_column', 'created_at');
$sortDirection = $request->input('sort_direction', 'desc');
$data = $this->automatedReportsService->listAskJiminnyReports($user, $sortColumn, $sortDirection);
return new JsonResponse($data);
} catch (Throwable $e) {
$this->logger->error('Failed to list Ask Jiminny reports', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch reports'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Get a single Ask Jiminny report.
*/
public function get(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
return new JsonResponse($this->automatedReportsService->get($uuid));
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to get Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getReportsCount(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$resultsCount = $this->automatedReportsService->getReportResults($report)->count();
return new JsonResponse(['count' => $resultsCount]);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to count report results', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'report_uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to count report results'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Delete an Ask Jiminny report.
*/
public function delete(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
if ($request->boolean('delete_generated_reports')) {
$this->automatedReportsService->deleteReportResults($uuid);
}
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$this->automatedReportsService->delete($uuid);
return new JsonResponse(null, Response::HTTP_NO_CONTENT);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to delete Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to delete report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getFilters(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$filters = $this->automatedReportsService->getAskJiminnyReportFilters($user);
return new JsonResponse(['filters' => $filters]);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report filters', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch filters'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
54929
|
|
54930
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\API\V2;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
class AskJiminnyReportsController extends Controller
{
public function __construct(
private readonly AutomatedReportsService $automatedReportsService,
private readonly LoggerInterface $logger,
) {
}
private function isNotOwnedByUser(AutomatedReport $report, User $user): bool
{
return $report->getTeamId() !== $user->getTeamId()
|| $report->getAttribute('created_by') !== $user->getId();
}
public function getFormData(Request $request, ?string $uuid = null): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $uuid ? $this->automatedReportsService->getReport($uuid) : null;
return new JsonResponse(
$this->automatedReportsService->getAskJiminnyReportFormData($user, $report)
);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report form data', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch form data'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Create a new Ask Jiminny report.
*/
public function create(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$data = $this->automatedReportsService->createAskJiminnyReport($request->all(), $user);
return new JsonResponse($data);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to create Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to create report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Update an existing Ask Jiminny report.
*/
public function update(Request $request, string $uuid): JsonResponse
{
\Illuminate\Support\Facades\Log::channel('custom_channel')->info("UPDATE");
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReport($report, $request->all(), $user);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to update Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to update report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Toggle Ask Jiminny report status (enable/disable).
*/
public function toggleStatus(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReportStatus(
$report,
(bool) $request->input('enabled'),
);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to toggle Ask Jiminny report status', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to toggle report status'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* List all Ask Jiminny reports.
*/
public function list(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$sortColumn = $request->input('sort_column', 'created_at');
$sortDirection = $request->input('sort_direction', 'desc');
$data = $this->automatedReportsService->listAskJiminnyReports($user, $sortColumn, $sortDirection);
return new JsonResponse($data);
} catch (Throwable $e) {
$this->logger->error('Failed to list Ask Jiminny reports', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch reports'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Get a single Ask Jiminny report.
*/
public function get(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
return new JsonResponse($this->automatedReportsService->get($uuid));
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to get Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getReportsCount(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$resultsCount = $this->automatedReportsService->getReportResults($report)->count();
return new JsonResponse(['count' => $resultsCount]);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to count report results', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'report_uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to count report results'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Delete an Ask Jiminny report.
*/
public function delete(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
if ($request->boolean('delete_generated_reports')) {
$this->automatedReportsService->deleteReportResults($uuid);
}
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$this->automatedReportsService->delete($uuid);
return new JsonResponse(null, Response::HTTP_NO_CONTENT);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to delete Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to delete report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getFilters(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$filters = $this->automatedReportsService->getAskJiminnyReportFilters($user);
return new JsonResponse(['filters' => $filters]);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report filters', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch filters'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
4
Previous Highlighted Error
Next Highlighted Error
{
"request_id":"822fa41b-afd3-43a9-a248-86b0e36f3131",
"status":"completed",
"timestamp":"2026-04-13T01:11:48.648399+00:00",
"s3_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131.MD",
"report_type":"coaching_profiles",
"podcast_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.txt",
"podcast_audio_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.mp3",
"podcast_ssml_url":"s3:\/\/jiminny.client-data\/5f0f4810-7e77-4086-8f69-93429ae4d70b\/reports\/822fa41b-afd3-43a9-a248-86b0e36f3131_podcast.ssml"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
54930
|
|
54949
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\API\V2;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
class AskJiminnyReportsController extends Controller
{
public function __construct(
private readonly AutomatedReportsService $automatedReportsService,
private readonly LoggerInterface $logger,
) {
}
private function isNotOwnedByUser(AutomatedReport $report, User $user): bool
{
return $report->getTeamId() !== $user->getTeamId()
|| $report->getAttribute('created_by') !== $user->getId();
}
public function getFormData(Request $request, ?string $uuid = null): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $uuid ? $this->automatedReportsService->getReport($uuid) : null;
return new JsonResponse(
$this->automatedReportsService->getAskJiminnyReportFormData($user, $report)
);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report form data', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch form data'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Create a new Ask Jiminny report.
*/
public function create(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$data = $this->automatedReportsService->createAskJiminnyReport($request->all(), $user);
return new JsonResponse($data);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to create Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to create report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Update an existing Ask Jiminny report.
*/
public function update(Request $request, string $uuid): JsonResponse
{
\Illuminate\Support\Facades\Log::channel('custom_channel')->info("UPDATE");
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReport($report, $request->all(), $user);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to update Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to update report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Toggle Ask Jiminny report status (enable/disable).
*/
public function toggleStatus(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReportStatus(
$report,
(bool) $request->input('enabled'),
);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to toggle Ask Jiminny report status', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to toggle report status'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* List all Ask Jiminny reports.
*/
public function list(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$sortColumn = $request->input('sort_column', 'created_at');
$sortDirection = $request->input('sort_direction', 'desc');
$data = $this->automatedReportsService->listAskJiminnyReports($user, $sortColumn, $sortDirection);
return new JsonResponse($data);
} catch (Throwable $e) {
$this->logger->error('Failed to list Ask Jiminny reports', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch reports'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Get a single Ask Jiminny report.
*/
public function get(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
return new JsonResponse($this->automatedReportsService->get($uuid));
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to get Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getReportsCount(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$resultsCount = $this->automatedReportsService->getReportResults($report)->count();
return new JsonResponse(['count' => $resultsCount]);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to count report results', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'report_uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to count report results'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Delete an Ask Jiminny report.
*/
public function delete(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
if ($request->boolean('delete_generated_reports')) {
$this->automatedReportsService->deleteReportResults($uuid);
}
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$this->automatedReportsService->delete($uuid);
return new JsonResponse(null, Response::HTTP_NO_CONTENT);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to delete Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to delete report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getFilters(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$filters = $this->automatedReportsService->getAskJiminnyReportFilters($user);
return new JsonResponse(['filters' => $filters]);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report filters', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch filters'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
[2026-04-20 09:25:26] local.INFO: UPDATE {"correlation_id":"082b343f-d8cb-46e7-a1dc-c68314e3fa7f","trace_id":"b7988fcf-12ec-4222-9859-0f479bea409f"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
54949
|
|
54950
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\API\V2;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
class AskJiminnyReportsController extends Controller
{
public function __construct(
private readonly AutomatedReportsService $automatedReportsService,
private readonly LoggerInterface $logger,
) {
}
private function isNotOwnedByUser(AutomatedReport $report, User $user): bool
{
return $report->getTeamId() !== $user->getTeamId()
|| $report->getAttribute('created_by') !== $user->getId();
}
public function getFormData(Request $request, ?string $uuid = null): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $uuid ? $this->automatedReportsService->getReport($uuid) : null;
return new JsonResponse(
$this->automatedReportsService->getAskJiminnyReportFormData($user, $report)
);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report form data', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch form data'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Create a new Ask Jiminny report.
*/
public function create(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$data = $this->automatedReportsService->createAskJiminnyReport($request->all(), $user);
return new JsonResponse($data);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to create Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to create report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Update an existing Ask Jiminny report.
*/
public function update(Request $request, string $uuid): JsonResponse
{
\Illuminate\Support\Facades\Log::channel('custom_channel')->info("UPDATE");
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReport($report, $request->all(), $user);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNPROCESSABLE_ENTITY);
} catch (Throwable $e) {
$this->logger->error('Failed to update Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to update report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Toggle Ask Jiminny report status (enable/disable).
*/
public function toggleStatus(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$data = $this->automatedReportsService->updateAskJiminnyReportStatus(
$report,
(bool) $request->input('enabled'),
);
return new JsonResponse($data);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to toggle Ask Jiminny report status', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to toggle report status'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* List all Ask Jiminny reports.
*/
public function list(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$sortColumn = $request->input('sort_column', 'created_at');
$sortDirection = $request->input('sort_direction', 'desc');
$data = $this->automatedReportsService->listAskJiminnyReports($user, $sortColumn, $sortDirection);
return new JsonResponse($data);
} catch (Throwable $e) {
$this->logger->error('Failed to list Ask Jiminny reports', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch reports'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Get a single Ask Jiminny report.
*/
public function get(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
return new JsonResponse($this->automatedReportsService->get($uuid));
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to get Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to fetch report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getReportsCount(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$resultsCount = $this->automatedReportsService->getReportResults($report)->count();
return new JsonResponse(['count' => $resultsCount]);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to count report results', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'report_uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to count report results'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
/**
* Delete an Ask Jiminny report.
*/
public function delete(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
if ($request->boolean('delete_generated_reports')) {
$this->automatedReportsService->deleteReportResults($uuid);
}
$report = $this->automatedReportsService->getReport($uuid);
if ($this->isNotOwnedByUser($report, $user)) {
return new JsonResponse(['error' => 'Report not found'], Response::HTTP_NOT_FOUND);
}
$this->automatedReportsService->delete($uuid);
return new JsonResponse(null, Response::HTTP_NO_CONTENT);
} catch (ModelNotFoundException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_NOT_FOUND);
} catch (Throwable $e) {
$this->logger->error('Failed to delete Ask Jiminny report', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
'uuid' => $uuid,
]);
return new JsonResponse(
['error' => 'Failed to delete report'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
public function getFilters(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$filters = $this->automatedReportsService->getAskJiminnyReportFilters($user);
return new JsonResponse(['filters' => $filters]);
} catch (Throwable $e) {
$this->logger->error('Failed to fetch Ask Jiminny report filters', [
'error' => $e->getMessage(),
'user_id' => $user->getId(),
]);
return new JsonResponse(
['error' => 'Failed to fetch filters'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
[2026-04-20 09:25:26] local.INFO: UPDATE {"correlation_id":"082b343f-d8cb-46e7-a1dc-c68314e3fa7f","trace_id":"b7988fcf-12ec-4222-9859-0f479bea409f"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
54950
|
|
67648
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
5
Previous Highlighted Error
Next Highlighted Error
[2026-04-20 09:25:26] local.INFO: UPDATE {"correlation_id":"082b343f-d8cb-46e7-a1dc-c68314e3fa7f","trace_id":"b7988fcf-12ec-4222-9859-0f479bea409f"}
[2026-04-20 09:30:50] local.INFO: UPDATE {"correlation_id":"abe7148a-42ad-4439-9741-e8f00b7d8f40","trace_id":"4feb175b-6a5d-40f2-8694-9d04a6faf3be"}
[2026-04-20 09:34:49] local.INFO: UPDATE {"correlation_id":"39a8d0f5-7ad7-43e8-9c4a-7660013bb5bc","trace_id":"2360889c-113d-4000-8b93-d36cadb1dad0"}
[2026-04-20 09:35:20] local.INFO: UPDATE {"correlation_id":"fe0182a7-8677-4f7a-b3ff-3b1cd5e9a049","trace_id":"767231c0-f5e2-4a5e-b282-b20c2670eb9a"}
[2026-04-21 12:16:07] local.INFO: ALL
Array
(
)
{"correlation_id":"a59da497-308d-4950-8778-777f7d10a382","trace_id":"8e4b5039-32b6-4efb-92a0-b55b6990efc4"}
[2026-04-21 12:18:13] local.INFO: ALL
Array
(
)
{"correlation_id":"fddd8339-6a75-4028-bd40-bb0076f94a95","trace_id":"e4c865e2-ac6a-4730-8928-34c89298e93e"}
[2026-04-21 12:18:50] local.INFO: ALL
Array
(
)
{"correlation_id":"36fd848b-30f1-4c90-aa55-9586c6a0fad5","trace_id":"3b5a2ffe-09ea-4fd9-8151-6c4c629ade7b"}
[2026-04-21 12:18:58] local.INFO: ALL
Array
(
)
{"correlation_id":"f33a303c-9162-4df3-8266-9bc183782cbd","trace_id":"746bf7b2-8ef3-41a7-808c-4087eab4806e"}
[2026-04-21 12:19:05] local.INFO: ALL
Array
(
)
{"correlation_id":"78139887-7a67-4f72-bdd5-7a4b9a4e37f3","trace_id":"e10aba59-b784-47fa-b73a-f34d2ed3e597"}
[2026-04-21 12:19:12] local.INFO: ALL
Array
(
)
{"correlation_id":"1151fb5f-8de2-4816-a77c-7901fcdb2003","trace_id":"1870e298-e087-42f5-8d10-d7efadca49b7"}
[2026-04-21 12:19:34] local.INFO: ALL
Array
(
)
{"correlation_id":"b9ef15c0-c806-45ce-96f7-f280d9cdc9fd","trace_id":"e1a1b5cb-acfe-4878-81d3-834bf6d75d0c"}
[2026-04-21 13:03:24] local.INFO: ALL
Array
(
)
{"correlation_id":"9e0a2502-40f8-4ad2-90d3-12e47317cc86","trace_id":"b8e765a3-e85a-44be-ada4-c0cfb2dbd951"}
[2026-04-21 13:04:17] local.INFO: ALL
Array
(
)
{"correlation_id":"2e9ba48a-664b-4704-9ea7-40f5940c3e7b","trace_id":"ecd9cf0e-83df-4f87-9d8e-e9fe90714348"}
[2026-04-21 13:04:39] local.INFO: ALL
Array
(
)
{"correlation_id":"da8a9f48-fd6f-49b7-95de-d4c367a56a14","trace_id":"3f58670c-4c1a-46dd-94d7-04d80ab1a44b"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
67648
|
|
67649
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
5
Previous Highlighted Error
Next Highlighted Error
[2026-04-20 09:25:26] local.INFO: UPDATE {"correlation_id":"082b343f-d8cb-46e7-a1dc-c68314e3fa7f","trace_id":"b7988fcf-12ec-4222-9859-0f479bea409f"}
[2026-04-20 09:30:50] local.INFO: UPDATE {"correlation_id":"abe7148a-42ad-4439-9741-e8f00b7d8f40","trace_id":"4feb175b-6a5d-40f2-8694-9d04a6faf3be"}
[2026-04-20 09:34:49] local.INFO: UPDATE {"correlation_id":"39a8d0f5-7ad7-43e8-9c4a-7660013bb5bc","trace_id":"2360889c-113d-4000-8b93-d36cadb1dad0"}
[2026-04-20 09:35:20] local.INFO: UPDATE {"correlation_id":"fe0182a7-8677-4f7a-b3ff-3b1cd5e9a049","trace_id":"767231c0-f5e2-4a5e-b282-b20c2670eb9a"}
[2026-04-21 12:16:07] local.INFO: ALL
Array
(
)
{"correlation_id":"a59da497-308d-4950-8778-777f7d10a382","trace_id":"8e4b5039-32b6-4efb-92a0-b55b6990efc4"}
[2026-04-21 12:18:13] local.INFO: ALL
Array
(
)
{"correlation_id":"fddd8339-6a75-4028-bd40-bb0076f94a95","trace_id":"e4c865e2-ac6a-4730-8928-34c89298e93e"}
[2026-04-21 12:18:50] local.INFO: ALL
Array
(
)
{"correlation_id":"36fd848b-30f1-4c90-aa55-9586c6a0fad5","trace_id":"3b5a2ffe-09ea-4fd9-8151-6c4c629ade7b"}
[2026-04-21 12:18:58] local.INFO: ALL
Array
(
)
{"correlation_id":"f33a303c-9162-4df3-8266-9bc183782cbd","trace_id":"746bf7b2-8ef3-41a7-808c-4087eab4806e"}
[2026-04-21 12:19:05] local.INFO: ALL
Array
(
)
{"correlation_id":"78139887-7a67-4f72-bdd5-7a4b9a4e37f3","trace_id":"e10aba59-b784-47fa-b73a-f34d2ed3e597"}
[2026-04-21 12:19:12] local.INFO: ALL
Array
(
)
{"correlation_id":"1151fb5f-8de2-4816-a77c-7901fcdb2003","trace_id":"1870e298-e087-42f5-8d10-d7efadca49b7"}
[2026-04-21 12:19:34] local.INFO: ALL
Array
(
)
{"correlation_id":"b9ef15c0-c806-45ce-96f7-f280d9cdc9fd","trace_id":"e1a1b5cb-acfe-4878-81d3-834bf6d75d0c"}
[2026-04-21 13:03:24] local.INFO: ALL
Array
(
)
{"correlation_id":"9e0a2502-40f8-4ad2-90d3-12e47317cc86","trace_id":"b8e765a3-e85a-44be-ada4-c0cfb2dbd951"}
[2026-04-21 13:04:17] local.INFO: ALL
Array
(
)
{"correlation_id":"2e9ba48a-664b-4704-9ea7-40f5940c3e7b","trace_id":"ecd9cf0e-83df-4f87-9d8e-e9fe90714348"}
[2026-04-21 13:04:39] local.INFO: ALL
Array
(
)
{"correlation_id":"da8a9f48-fd6f-49b7-95de-d4c367a56a14","trace_id":"3f58670c-4c1a-46dd-94d7-04d80ab1a44b"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
67649
|
|
67650
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
5
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
67650
|
|
70785
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
109
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService): void
{
$report = AutomatedReportResult::find(285);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70785
|
|
70789
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
109
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService): void
{
$report = AutomatedReportResult::find(285);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
[2026-04-22 11:23:14] local.INFO: $payload
Array
(
[user_question] => Are these activities and give me the most insightful information about them
[call_ids] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[team_id] => 1
[request_id] => d3037407-091e-438a-8c8e-6745c5bf8df9
[callback_url] => https://qatest:[EMAIL]/webhook/reports/ready
[report_period] => 21 Apr 2026
[report_name] => Search One
)
{"correlation_id":"c786229a-00f6-4af1-b4ec-7553791bafe4","trace_id":"d4cfff0e-85bf-4b35-a8c0-d0752235ffc4"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70789
|
|
70809
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
[2026-04-22 11:23:14] local.INFO: $payload
Array
(
[user_question] => Are these activities and give me the most insightful information about them
[call_ids] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[team_id] => 1
[request_id] => d3037407-091e-438a-8c8e-6745c5bf8df9
[callback_url] => https://qatest:[EMAIL]/webhook/reports/ready
[report_period] => 21 Apr 2026
[report_name] => Search One
)
{"correlation_id":"c786229a-00f6-4af1-b4ec-7553791bafe4","trace_id":"d4cfff0e-85bf-4b35-a8c0-d0752235ffc4"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70809
|
|
70829
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
109
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService): void
{
$report = AutomatedReportResult::find(285);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70829
|
|
70858
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70858
|
|
70859
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
8
1
1
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Jiminny\Traits\RequiresUUID;
/**
* Jiminny\Models\AutomatedReportResult
*
* @property int $id
* @property string $uuid
* @property int $report_id
* @property string|null $name
* @property int $status
* @property int $reason
* @property string $media_type
* @property int|null $parent_id
* @property array|null $payload
* @property array|null $response
* @property Carbon|null $requested_at
* @property Carbon|null $generated_at
* @property Carbon|null $sent_at
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read \Jiminny\Models\AutomatedReport $report
* @property-read AutomatedReportResult|null $parent
* @property-read \Illuminate\Database\Eloquent\Collection<int, AutomatedReportResult> $children
*/
class AutomatedReportResult extends Model
{
use RequiresUUID;
/**
* Status constants
*/
public const int STATUS_DEFAULT = 0;
public const int STATUS_REQUESTED = 1;
public const int STATUS_GENERATED = 2;
public const int STATUS_SENT = 3;
public const int STATUS_FAILED = 4;
/**
* Reason constants
*/
public const int REASON_DEFAULT = 0;
public const int REASON_NOT_ENOUGH_ACTIVITIES = 1;
public const int REASON_PROPHET_API_ERROR = 2;
protected $table = 'automated_report_results';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'report_id',
'name',
'status',
'reason',
'media_type',
'parent_id',
'payload',
'response',
'requested_at',
'generated_at',
'sent_at',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'payload' => 'array',
'response' => 'array',
'requested_at' => 'datetime',
'generated_at' => 'datetime',
'sent_at' => 'datetime',
];
}
/**
* Get the automated report that owns this result.
*
* @return BelongsTo
*/
public function report(): BelongsTo
{
return $this->belongsTo(AutomatedReport::class, 'report_id')->withTrashed();
}
/**
* Get the parent report result.
*
* @return BelongsTo
*/
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
/**
* Get the child report results.
*
* @return HasMany
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* Get the ID of the automated report result.
*
* @return int
*/
public function getId(): int
{
return $this->getAttribute('id');
}
/**
* Get the UUID of the automated report result.
*
* @return string
*/
public function getUuid(): string
{
return $this->getAttribute('id_string');
}
/**
* Get the report ID of the automated report result.
*
* @return int
*/
public function getReportId(): int
{
return $this->getAttribute('report_id');
}
/**
* Get the name of the automated report result.
*
* @return ?string
*/
public function getName(): ?string
{
return $this->getAttribute('name');
}
/**
* Get the status of the automated report result.
*
* @return int
*/
public function getStatus(): int
{
return $this->getAttribute('status');
}
/**
* Get the reason of the automated report result.
*
* @return int
*/
public function getReason(): int
{
return $this->getAttribute('reason');
}
/**
* Get the media type of the automated report result.
*
* @return string
*/
public function getMediaType(): ?string
{
return $this->getAttribute('media_type');
}
/**
* Get the parent ID of the automated report result.
*
* @return int|null
*/
public function getParentId(): ?int
{
return $this->getAttribute('parent_id');
}
/**
* Get the payload of the automated report result.
*
* @return array|null
*/
public function getPayload(): ?array
{
return $this->getAttribute('payload');
}
/**
* Get the response of the automated report result.
*
* @return array|null
*/
public function getResponse(): ?array
{
return $this->getAttribute('response');
}
/**
* Get the requested at date of the automated report result.
*
* @return Carbon|null
*/
public function getRequestedAt(): ?Carbon
{
return $this->getAttribute('requested_at');
}
/**
* Get the generated at date of the automated report result.
*
* @return Carbon|null
*/
public function getGeneratedAt(): ?Carbon
{
return $this->getAttribute('generated_at');
}
/**
* Get the sent at date of the automated report result.
*
* @return Carbon|null
*/
public function getSentAt(): ?Carbon
{
return $this->getAttribute('sent_at');
}
/**
* Get the created at date of the automated report result.
*
* @return Carbon
*/
public function getCreatedAt(): Carbon
{
return $this->getAttribute('created_at');
}
/**
* Get the updated at date of the automated report result.
*
* @return Carbon
*/
public function getUpdatedAt(): Carbon
{
return $this->getAttribute('updated_at');
}
/**
* Check if the report result is in requested status.
*
* @return bool
*/
public function isRequested(): bool
{
return $this->getStatus() === self::STATUS_REQUESTED;
}
/**
* Check if the report result is in generated status.
*
* @return bool
*/
public function isGenerated(): bool
{
return $this->getStatus() === self::STATUS_GENERATED;
}
/**
* Check if the report result is in sent status.
*
* @return bool
*/
public function isSent(): bool
{
return $this->getStatus() === self::STATUS_SENT;
}
/**
* Check if the report result is in failed status.
*
* @return bool
*/
public function isFailed(): bool
{
return $this->getStatus() === self::STATUS_FAILED;
}
public function getStatusLabel(): string
{
return match ($this->getStatus()) {
self::STATUS_REQUESTED => 'Requested',
self::STATUS_GENERATED => 'Generated',
self::STATUS_SENT => 'Sent',
self::STATUS_FAILED => 'Failed',
default => 'Default',
};
}
public function getReport(): AutomatedReport
{
return $this->getAttribute('report');
}
public function getFromDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['from_date'])) {
return null;
}
return Carbon::parse($payload['from_date']);
}
public function getToDate(): ?Carbon
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['to_date'])) {
return null;
}
return Carbon::parse($payload['to_date']);
}
public function getReportType(): ?string
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['report_type'])) {
return null;
}
return $payload['report_type'];
}
public function getGroups(): array
{
$payload = $this->getPayload();
if (empty($payload) || empty($payload['group_ids'])) {
return [];
}
return $payload['group_ids'];
}
public function getPdfUrl(): ?string
{
$response = $this->getResponse();
return $response['pdf_url'] ?? null;
}
public function getPodcastAudioUrl(): ?string
{
$response = $this->getResponse();
return $response['podcast_audio_url'] ?? null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70859
|
|
70943
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
7/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Analyzing…
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70943
|
|
70945
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp• •DOCKER• 81docker-zshX3compiledeventsroutesviewsjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00:stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5:jiminny-worker-processing-5_00: stoppedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-audio:worker-audio_00:stoppedworker-crm-update:worker-crm-update_00:stoppedworker-download:worker-download_00: stoppedworker-nudges:worker-nudges_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-sync:worker-crm-sync_00:stoppedworker-emails:worker-emails_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-es-update:worker-es-update_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00: startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny# php artisan jiminny:debugroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugroot@docker_lamp_1:/home/jiminny# php artisan jiminny:debug§ Support Daily • in 15 m100% <7docker* Build full day ac... • X4screenpipe"• 885-zsh86APP (-zsh)5.74ms DONE7.47ms DONE5.45ms DONE41.25ms DONE87Wed 22 Apr 14:45:20181ec2-user@ip-10-...• *8|+...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70945
|
|
70960
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
7/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70960
|
|
70972
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
110
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService): void
{
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70972
|
|
70975
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
1
110
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService): void
{
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
70975
|
|
71013
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
DMSActivityMorePhpStormcaltViewINavigareCodeJiminny...vXStarredi• jiminny-x-integrati..8 platform-inner-team# Channelsic al-chaoter# alerts# backend# c-learning-people# confusion-clinic# curiosity_lab# deal-insights-dev# engineering# frontend# general# infra-changes# jiminny-bgA neonle-with-copilo…• people-with-zoom-….# platform-team# platform-tickets# product launchesi random# releases# sofia-officeLaravelKeractorlools• plattorm-inner-...Q 10Channel OverviewMoreinny/app Today at 12:24 PM| Added byNikolay Nikolov 12:42 PMмахнах и delay-a на webhooks, но, самоирааlе са тези които пишат в гедис, има идруги които извьршват деиствия - merzе.Nikolay Nikolov 2:39 PMсьздадох един таск - грешката е спояла.https://iiminny.atlassian.net/browse/JY-20728JY-20/28 Hubsbotl Find the root cause of47 nit and tweak A? client rateStatus: BacklogType: StoryAssignee: UnassignedT Priority: Mediumsync thread*+ Al SummariseNikolay Ivanov 2:42 PM[URL_WITH_CREDENTIALS] LUG PREFIX .I•Fetched activity IDs'. П'automatedReportluid' => Sthis->reportUuid.'activitycount' => count Sactivitvids).if count(Sactivitvids) < self.MIN ACTIVITTES COUNT) <AutomatedRenortResult:REASON NOT_ ENOUGH ACTTVTTTES)•Sloaden->infolself::L0G PREFTXNot enouah activities. skinned'lautomatedPenontlludd' =s Cthic-snenontllurd= count (Cactivitvide)thic-sdicnatchMotGenenatodNotjficatjonc/CautomatedReport,SreportService,Sunl Generator.snobuisparcher,Sloagerreturn:spavload = SrenortService->qetAskJaminnvGenerateReportPavload0automatedRenort: SautomatedRenort# Support Daily - in 10 m100% CWed 22 Apr 14:50:46U AskJiminnyReportActivityServiceTest v« HS_local [jiminny@localhost]& console [PROD]& console [EU]= laravel.log4 SF jiminny@localhost]A console [STAGING]W Windsurf Toamio 4 space:...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71013
|
|
71014
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:...
|
iTerm2
|
faVsco.js – custom.log
|
NULL
|
71014
|
|
71015
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
iTerm2
|
faVsco.js – custom.log
|
NULL
|
71015
|
|
71016
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
iTerm2
|
faVsco.js – custom.log
|
NULL
|
71016
|
|
71017
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71017
|
|
71018
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71018
|
|
71019
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71019
|
|
71020
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71020
|
|
71021
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71021
|
|
71022
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71022
|
|
71023
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71023
|
|
71354
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
{"user_question":"What objections are prospects raising most often, and how is Becky responding to each one? For each objection:\n- State the objection topic (e.g. price, timing, competitor preference)\n- Give a short example of how Becky handled it\n- Flag whether the handling was effective or if it stalled the conversation\n\nKeep the output structured with one section per objection type. Maximum 5 objections. Use bullet points within each section.","call_ids":["78439498","78404730","78492560","78338328"],"team_id":1,"request_id":"fc98807e-2ce7-44e6-b820-0df9252262aa","callback_url":"https:\/\/team:[EMAIL]\/webhook\/reports\/ready","report_period":"15 - 21 Apr 2026","report_name":"Becky's Objection Handling Report"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71354
|
|
71355
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
{"user_question":"What objections are prospects raising most often, and how is Becky responding to each one? For each objection:\n- State the objection topic (e.g. price, timing, competitor preference)\n- Give a short example of how Becky handled it\n- Flag whether the handling was effective or if it stalled the conversation\n\nKeep the output structured with one section per objection type. Maximum 5 objections. Use bullet points within each section.","call_ids":["78439498","78404730","78492560","78338328"],"team_id":1,"request_id":"fc98807e-2ce7-44e6-b820-0df9252262aa","callback_url":"https:\/\/team:[EMAIL]\/webhook\/reports\/ready","report_period":"15 - 21 Apr 2026","report_name":"Becky's Objection Handling Report"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71355
|
|
71356
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
iTerm2ShellEditViewSessionScriptsProfilesWindowHelpAl chapter • in 1h 34 mA100% C7Wed 22 Apr 15:26:22PROD (ssh)181DOCKER881docker882-zsh* Build full day ac...• 84|805efb160ee8d9da02e60364ace7970eb2b35f31" "$?") > '/dev/null' 2>&1 &docker_lamp_12026-04-22 12:25:28 Running ['artisan' hubspot: journal-poll --start]in background1.47ms DONEdocker_lamp_1• ('/usr/local/bin/php' 'artisan' hubspot: journal-poll --start > '7proc/1/fd/1' 2>&1 ; '/usr/local/bin/php'schedule:finish "framework/schedule-e26d77f915d2c55fe91ca4148a230e32eaa1865e" "$?") >dev/nulldocker_lamp_12026-04-22 12:25:28 Running ['artisan'crm: bullhorn:ping --heartbeat]account(s) to be processeddocker_lamp_1docker_lamp_1docker_lamp_1docker_lamp_11 Done!3S DONEi Starting HubSpot journal polling service...docker_1amp_11 '/usr/local/bin/php' 'artisan' crm:bullhorn:ping--heartbeat > '/proc/1/fd/1'2>&1docker_lamp_1docker_lamp_1run_artisan_schedule: Done waiting for schedule:rundocker_lamp_1docker_lamp_1 |2026-04-22 12:26:02 Running ['artisan' meeting-bot: schedule-bot] …..1s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' meeting-bot:schedule-bot › */proc/1/fd/1'2>&1docker_lamp_12026-04-22 12:26:04 Running ['artisan' dialers:monitor-activities] .1s DONEdocker_1amp_11 '/usr/local/bin/php' 'artisan' dialers:monitor-activities › */proc/1/fd/1'2>&1docker_lamp_12026-04-22 12:26:06 Running ['artisan' jiminny:monitor-social-account2s DONEdocker_lamp_111 '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > */proc/1/fd/1'2>&1docker_lamp_12026-04-22 12:26:08 Running ['artisan' mailbox:skip-lists:refresh].1s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:skip-lists:refresh › '/proc/1/fd/1'2>81docker_lamp_12026-04-22 12:26:09 Running ['artisan'mailbox:batch:process--max-batches=15]2sDONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:batch:process --max-batches=15 >*/proc/1/fd/1' 2>&1docker_lamp_1/2026-04-22 12:26:12 Running ['artisan'conference:monitor:count]8S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' conference:monitor: count > '/proc/1/fd/1' 2>&1View in Docker Desktop• View ConfigEnable Watchscreenpipe"O 885-zsh86PROD (ssh)Run'do-release-upgrade' to upgrade to it.APP (-zsh)ec2-user@ip-10-...• *8|+PROD*** System restart required ***Last login: Wed Apr 22 08:09:38 2026 from 212.5.153.87lukas@jiminny-prod-bastion:~$ 0X L3 EU (ssh)New release '24.04.4 LTS' available.Run'do-release-upgrade'to upgrade to it.*** System restart required ***Last login: Tue Apr 21 16:24:08 2026 from 212.5.153.87lukas@jiminny-eu-bastion:~$ |T4 STAGE (-zsh)Run 'do-release-upgrade' to upgrade to it.STAGELast login: Thu Apr 16 07:34:39 2026 from [IP_ADDRESS]: $ client_loop: send disconnect: Broken pipelukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $T5 QA (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentsX T6 FE (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsFRONTENDPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IX Y7 EXT (-zsh)Last login: Mon Apr 20 19:48:04 on ttys005Poetry could not find a pyproject.toml file in /Users/lukas or its parents EXTENSIONPoetry could not find a pyproject.toml file in /Users/lukas or its parentsukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71356
|
|
71357
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
ActivityMorePhpStormViewINavigareJiminny...vTMore unreads# ar-cnapter# alerts# backend# c-learning-people# contusion-clinic# curiosity labdeal-insichts-dev# engineering# frontend# general# infra-changes#t liminnv-be• people-with-copilo..8 people-with-zoom-….# platform-team# platform-tickets# product launches# random# releases# sofa-office# supporti thank-vous# the people of jimi..ó- Direct messagesStoyan Tomov3 Aneliya Angelova, ...Al Anoliva AnaolovsCodeLaravelKeractorTOOISStoyan Tomov• MessagesAdd canvasUr FilesMonday, April zUthvStoyan Tomov 3:13 PMздрастиscheduled срещи би трябвало да влизат вDeal Insights в timeline-a на сделките с коитоса асоциирани нали така.Lukas Kovalik 3:21 PMздрасти, не ояха май само приключениstatus comnletedunu delivered. received aa emailStovan Tomoy 3:23 PMне мисля. спорел dеal risк-овете би трябвалои scheduled конферениии ла влизат тамeduled - It there are no tuture activзащо иначе бихме го гледали като рискLukas Kovallik 3-23 PMте са различно нещотам глеламе на schedulledMessage Stoyan Tomov+ АaWindowmelp#Al chapter - in 1h 34 m100% CWed 22 Apr 15:26:24Fv faVsco.jsv°9 JY-20157-AJ-report-not-send-notificationAsk.JiminnvReportActivityServicelestvProject v© RequestGenerateAskJiminnyReportJob.php>C) AutomatedReportsservice.onp= custom.loa XI4 SF jiminny@localhost]A HS_local jiminny@localhost]« console [PROD).gitignoreeaudio.wav© AutomatedReportsCommand.phpsenakeportnotcenerateamallJoo.ongReportNotGenerated.phpreport-not-generated.blade.phpA console [EU# concole [STAGiNG)pnp apL_vz.phppic (e.{• price, timing, competitor preference)\n- Give a short example of how Becky handled it\n- m ve= hubspot-journal-poll.logE laravel.log< phpunit.xmlis ttt.isE oauth-private.kevE oauth-public.key= storageLOG PREFIXW.*3/16TJ Y := supervisord.pid•text-relav..sontestsFeature> Intearationi> 7 Servicesv D Unit.MActions• M ComnonentM Confiauration• M concole• MContracts› D Domair> DDTO› D Enums› D Events› D ExceptionsServicesv _ Databasev A liminnv@localhosiV A PROD# console 1 s218 mslDockerclass RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUniquepublic function handle(A1V3 ^149150151152153= SreportService->getReportF1LeName(Sthis->reportResult),'pavload' => Soavload.i'status' => AutomatedReportResult::STATUS REQUESTED.'requested_at' => Carbon::now)->toDateTimeString.$logger->info(self::L0G_PREFIX .Request sent','automatedReportUuid' => $this->reportUuid,'reportUuid' => $this->reportResult->getUuid.'payload' => Spayload.161$response = $prophetClient->sendRequest(enapoint. rrocheruulent..AoK JLMINNY KEPURI163requescarray. soayload,165166167$logger->info(self::L0G_PREFIX' Response received'. [= Sresponse->gettontento,101071171Outputiii liminny automated renort recultem 1rowv# Q EAAOuuid (UUID with time-low and time-high swapped)fc98807e-2ce7-44e6-h820-0df9252262aalW report_id(™ nameBecky's Objection Handling Report - 15 - 21 Apr 20261• media typepdfM parent id<null>M statusJim reasonCsvu|d 1 →|0,8,I payloadI responseF"usen question": "What obiections are prospects raising most often, and how is Becky responding to each one? For each obiection:\n- State the obiection topic (e.g. price, timing, competitor preferenceE{"request id":"fc98807e-2ce7-44e6-b820-0df9252262aa", "status": "completed" "timestamp": "2026-04-22T11:41:36.159953+00:00", "s3 Url":"s3:V/iliminny.client-dataV/5f0f4810-7e77-4086-8f69-93429ae4d70bV/panrequested_at2026-04-22 11:41:25W generated_at2026-04-22 11:41:36W sent_at2026-04-22 11-42•36created at2026-04-22 11:41:25updated at2026-04-22 11:42:36W Windsurf Teams 1:750 UTF-8 f 4 spaces...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71357
|
|
71358
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
{"user_question":"What objections are prospects raising most often, and how is Becky responding to each one? For each objection:\n- State the objection topic (e.g. price, timing, competitor preference)\n- Give a short example of how Becky handled it\n- Flag whether the handling was effective or if it stalled the conversation\n\nKeep the output structured with one section per objection type. Maximum 5 objections. Use bullet points within each section.","call_ids":["78439498","78404730","78492560","78338328"],"team_id":1,"request_id":"fc98807e-2ce7-44e6-b820-0df9252262aa","callback_url":"https:\/\/team:[EMAIL]\/webhook\/reports\/ready","report_period":"15 - 21 Apr 2026","report_name":"Becky's Objection Handling Report"}
Project
Project
New File or Directory…...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71358
|
|
71359
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
1
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AskJiminnyReportActivityService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
private const string LOG_PREFIX = '[AskJiminnyReport:Generate]';
private const int MIN_ACTIVITIES_COUNT = 1;
public int $tries = 2;
private ?AutomatedReportResult $reportResult = null;
public function __construct(private readonly string $reportUuid)
{
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
AskJiminnyReportActivityService $activityService,
ProphetClient $prophetClient,
LoggerInterface $logger,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
): void {
$logger->info(self::LOG_PREFIX . ' Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport($this->reportUuid);
// $this->dispatchNotGeneratedNotifications(
// $automatedReport,
// $reportService,
// $urlGenerator,
// $jobDispatcher,
// $logger,
// );
//
// return;
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$creator = $automatedReport->getCreator();
if ($creator === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, report creator not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$savedSearch = $automatedReport->getSavedSearch();
if ($savedSearch === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, saved search not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$prompt = $automatedReport->getAskAnythingPrompt();
if ($prompt === null) {
$logger->warning(self::LOG_PREFIX . ' Skipped, ask anything prompt not found', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$this->reportResult = $reportService->getOrCreateReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
$activityIds = $activityService->getActivityIdsForSavedSearch(
savedSearch: $savedSearch,
user: $creator,
frequency: $automatedReport->getFrequency(),
);
$logger->info(self::LOG_PREFIX . ' Fetched activity IDs', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
if (count($activityIds) < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'activityCount' => count($activityIds),
]);
$this->dispatchNotGeneratedNotifications(
$automatedReport,
$reportService,
$urlGenerator,
$jobDispatcher,
$logger,
);
return;
}
$payload = $reportService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $this->reportResult,
activityIds: $activityIds,
);
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'payload' => $payload,
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => Carbon::now()->toDateTimeString(),
]);
$logger->info(self::LOG_PREFIX . ' Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::ASK_JIMINNY_REPORT,
requestArray: $payload,
);
$logger->info(self::LOG_PREFIX . ' Response received', [
'response' => $response->getContent(),
]);
} catch (Throwable $exception) {
$reason = $exception instanceof ProphetException
? AutomatedReportResult::REASON_PROPHET_API_ERROR
: AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(self::LOG_PREFIX . ' Error', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult?->getUuid(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if ($automatedReport->getType() !== AutomatedReportsService::TYPE_ASK_JIMINNY) {
$logger->warning(self::LOG_PREFIX . ' Skipped, not an ask_jiminny report', [
'automatedReportUuid' => $this->reportUuid,
'type' => $automatedReport->getType(),
]);
return false;
}
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
$this->reportResult?->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
private function dispatchNotGeneratedNotifications(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService,
UrlGenerator $urlGenerator,
JobDispatcherInterface $jobDispatcher,
LoggerInterface $logger,
): void {
if ($this->reportResult === null) {
return;
}
$recipients = $reportService->getValidRecipientUsers($automatedReport);
if (empty($recipients)) {
$logger->info(self::LOG_PREFIX . ' No recipients to notify about missing report', [
'automatedReportUuid' => $this->reportUuid,
]);
return;
}
$reportName = $automatedReport->getCustomName()
?: $reportService->getReportTypeName($this->reportResult);
$periodName = $reportService->getReportPeriodName($this->reportResult);
$reportsPageUrl = $urlGenerator->route('ai.reports.show');
foreach ($recipients as $recipient) {
$jobDispatcher->dispatch(new SendReportNotGeneratedMailJob(
reportUuid: $this->reportResult->getUuid(),
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
reportName: $reportName,
periodName: $periodName,
reportsPageUrl: $reportsPageUrl,
));
}
$logger->info(self::LOG_PREFIX . ' Dispatched not-generated notifications', [
'automatedReportUuid' => $this->reportUuid,
'recipientsCount' => count($recipients),
]);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
{"user_question":"What objections are prospects raising most often, and how is Becky responding to each one? For each objection:\n- State the objection topic (e.g. price, timing, competitor preference)\n- Give a short example of how Becky handled it\n- Flag whether the handling was effective or if it stalled the conversation\n\nKeep the output structured with one section per objection type. Maximum 5 objections. Use bullet points within each section.","call_ids":["78439498","78404730","78492560","78338328"],"team_id":1,"request_id":"fc98807e-2ce7-44e6-b820-0df9252262aa","callback_url":"https:\/\/team:[EMAIL]\/webhook\/reports\/ready","report_period":"15 - 21 Apr 2026","report_name":"Becky's Objection Handling Report"}
Project
Project
New File or Directory…
Expand Selected...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71359
|
|
71360
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
ActivityMorePhpStormcaltViewINavigareJiminny...vTMore unreads# arcnapter# alerts# backend# c-learning-people# contusion-clinic# curiosity_labdeal-insichts-dev# engineering# frontend# general# infra-changes#t liminnv-be• people-with-copilo..8 people-with-zoom-….# platform-team# platform-tickets# product launches# random# releases# sofa-office# supporti thank-vous# the people of jimi..ó- Direct messagesStoyan Tomov3 Aneliya Angelova, ...Al Anoliva AnaolovsCodeLaravelKeractorTOOISStoyan Tomov• MessagesAdd canvasur FilesMonday, April 20thStoyan Tomov 3:13 PMздрастиscheduled срещи би трябвало да влизат вDeal Insights в timeline-a на сделките с коитоса асоциирани нали така.Lukas Kovalik 3:21 PMздрасти, не ояха май само приключениstatus comnletedunu delivered. received aa emailStovan Tomoy 3:23 PMне мисля. спорел dеal risк-овете би трябвалои scheduled конферениии ла влизат тамeduled - It there are no tuture activзащо иначе бихме го гледали като рискLukas Kovallik 3-23 PMте са различно нещотам глеламе на schedulledMessage Stoyan Tomov+ АaWindowmelp$08Al chapter . in 1h 21m100% CWed 22 Apr 15:26:42Fv faVsco.jsv°9 JY-20157-AJ-report-not-send-notificationAsk.JiminnvReportActivityServicelestvProject v© RequestGenerateAskJiminnyReportJob.php>C) AutomatedReportsservice.onp= custom.loa XI4 SF jiminny@localhost]A HS_local jiminny@localhost]console [PROD.gitignoreeaudio.wav© AutomatedReportsCommand.phpsenakeportnotcenerateamallJoo.ongReportNotGenerated.phpreport-not-generated.blade.php•# concole [Cull# concole [STAGiNG)pnp apL_vz.phpk\/reports\/ready" "report_period":"15 - 21 Apr 2026" "report_name":"Becky's Objection Handling Repc ~= hubspot-journal-poll.logE laravel.log< phpunit.xmlis ttt.isE oauth-private.kevE oauth-public.key= storageLOG PREFIXw.*3/16TJ Y := supervisord.pid•text-relav..sontestsaFeature> Intearationi> 7 Servicesv D Unit.MActions• M ComnonentM Confiauration• M concole• MContracts› D Domair> DDTO› D Enums› D Events› D ExceptionsServicesv _ Databasev A liminnv@localhosiV A PRODDocker# console 1 s218 mslclass RequestGenerateAskJiminnyReportJob implements ShouldQueue, ShouldBeUniquepublic function handle(A1X3 ,149150151152153= SreportService->getReportF1LeName(Sthis->reportResult),'pavload' => Soavload.i'status' => AutomatedReportResult::STATUS REQUESTED.'requested_at' => Carbon::now)->toDateTimeString.$logger->info(self::L0G_PREFIX .Request sent','automatedReportUuid' => $this->reportUuid,'reportUuid' => $this->reportResult->getUuid.'payload' => Spayload.161$response = $prophetClient->sendRequest(enapoint. rrocheruulent..AoK JLMINNY KEPURI163requescarray. soayload,165$logger->info(self::L0G_PREFIX' Response received'. [167= Sresponse->gettontento,101071171Outputiii liminny automated renort recultem 1rowv# Q EAAOuuid (UUID with time-low and time-high swapped)fc98807e-2ce7-44e6-h820-0df9252262aalW report_id(™ name1• media typeBecky's Objection Handling Report - 15 - 21 Apr 2026pdfM parent id<null>l statusJim reasonCsvu|d 1 →|0,8,I payloadI responseF"usen question": "What obiections are prospects raising most often, and how is Becky responding to each one? For each obiection:\n- State the obiection topic (e.g. price, timing, competitor preferenceE{"request id":"fc98807e-2ce7-44e6-b820-0df9252262aa", "status": "completed" "timestamp": "2026-04-22T11:41:36.159953+00:00", "s3 Url":"s3:V/iliminny.client-dataV/5f0f4810-7e77-4086-8f69-93429ae4d70bV/panrequested_at2026-04-22 11:41:25W generated_at2026-04-22 11:41:36W sent_at2026-04-22 11-42•36created at2026-04-22 11:41:25updated at2026-04-22 11:42:36W Windsurf Teams 1:711 UTF-8 f 4 spaces...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71360
|
|
71361
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
LOG_PREFIX
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
3/16
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp• 0PROD (ssh)DOCKER881docker882-zsh* Build full day ac..• ×4XI11DOCKER (docker-compose)docker_1amp_Aircall Demo (7980eSeb-b11c-4cee-9c21-8bb29ba85f3b) is not yet assignedan owner.skipping…..docker_1amp_11 [HubSpot] Syncing objects for GoStudent UAT (b2d49a54-b645-4637-a7ae-a86cfce6e8e4) since 2026-02-17 15:09:59(delay: (1s)docker_lamp_11 [HubSpot] Syncing objects for JustCall (c6b9d6b0-b48d-4832-a68c-a57d60651888) since 2026-02-17 15:07:41 (delay: 1s)docker_lamp_11 Team Twilio Video (c334ca55-b230-411c-b10e-31c8204bd07b) is not yet assignedanowner.skipping…..docker_lamp_11 Team My Test Account 3000 (dbc9990d-b35f-4e38-9550-22cdd6059514) is notyet assigned an owner.docker_lamp_11 Team test (7997eb70-8aa4-491a-870d-311977568df4) is not yet assigned anowner.skipping…..docker_1amp_1I Team Test (5e06dcee-0613-470e-9a77-2c283198f3bf) is not yet assigned anowner.skipping...docker_lamp_1I Team testogg autosync (da44776e-306f-427a-83d8-a1b4baa5537e) is not yet assigned an owner. skipping...docker_1amp_1I Team Tourlaner (d9b71080-388b-4cf5-8175-aa0f29bee635) is not yet assigned an owner. skipping...docker_lamp_11 Dispatched 4 HubSpot sync jobsdocker_lamp_19S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' crm:sync-hubspot-objects › '/proc/1/fd/1' 2>&1docker_1amp_12026-04-22 12:26:29 Running ['artisan' activity:notify-not-logged]2026-04-22 12:26:30 Jiminny\Jobs\Crm\SyncHubspot0bjectsdocker_lamp_12026-04-22 12:26:31 Jiminny\Jobs\Crm\SyncHubspotObjects935.27ms DONEdocker_lamp_12026-04-22 12:26:31 Jiminny\Jobs\Crm\SyncHubspotObjectsRUNNINGdocker_lamp_12026-04-22 12:26:31 Jiminny\Jobs\Crm\SyncHubspotObjects180.28ms DONEdocker_lamp_12026-04-22 12:26:31 Jiminny\Jobs\Crm\SyncHubspotObjectsRUNNINGdocker_1amp_12026-04-22 12:26:31 Jiminny\Jobs\Crm\SyncHubspotObjects126.88msDONEdocker_lamp_12026-04-22 12:26:31 Jiminny\Jobs\Crm\SyncHubspotObjectsRUNNINGdocker_lamp_12026-04-22 12:26:31 Jiminny\Jobs\Crm\SyncHubspotObjects82.29mS DONEdocker_lamp_110s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' activity:notify-not-logged › */proc/1/fd/1' 2>&1View in Docker Desktopo View ConfigEnable WatchAl chapter • in 1h 34 mA100% C7Wed 22 Apr 15:26:44screenpipe"O ₴5-zsh8612 PROD (ssh)'do-release-upgrade' to upgrade to it.APP (-zsh)181ec2-user@ip-10-...• *8|+PROD*** System restart required ***Last login: Wed Apr 22 08:09:38 2026 from 212.5.153.87lukas@jiminny-prod-bastion:~$ 0X T3 EU (ssh)New release '24.04.4 LTS' available.Run'do-release-upgrade'to upgrade to it.*** System restart required ***Last login: Tue Apr 21 16:24:08 2026 from 212.5.153.87lukas@jiminny-eu-bastion:~$ |XIT4 STAGE (-zsh)Run 'do-release-upgrade' to upgrade to it.STAGELast login: Thu Apr 16 07:34:39 2026 from [IP_ADDRESS]: $ client_loop: send disconnect: Broken pipelukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $T5 QA (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentsX T6 FE (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsFRONTENDPoetry could not find a pyproject.toml file in /Users/lukas or its parentsLukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IX Y7 EXT (-zsh)Last login: Mon Apr 20 19:48:04 on ttys005Poetry could not find a pyproject.toml file in /Users/lukas or its parents EXTENSIONPoetry could not find a pyproject.toml file in /Users/lukas or its parentsukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71361
|
|
71568
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
102
3
34
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Kiosk\AutomatedReports;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\ActivitySearch\FilterDefinition\InputTypeEnum;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateReportJob;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Throwable;
class AutomatedReportsService
{
public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';
public const string TYPE_ASK_JIMINNY = 'ask_jiminny';
/**
* Standard report types (used by kiosk for existing automated reports).
*/
// @TODO this will add filter, however if we need to control feature by FF we need conditional logic
public const array TYPES = [
['id' => 'exec_summary', 'name' => 'Exec Summary'],
['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],
['id' => 'product_feedback', 'name' => 'Product Feedback'],
['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],
// ['id' => 'questions', 'name' => 'Questions'],
// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],
];
public const array ALL_TYPES = [
...self::TYPES,
['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],
];
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
/**
* Frequencies for standard (non-Ask Jiminny) reports.
*/
public const array FREQUENCIES = [
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
/**
* Frequencies for Ask Jiminny reports.
*/
public const array ASK_JIMINNY_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
];
public const string MEDIA_TYPE_PDF = 'pdf';
public const string MEDIA_TYPE_PODCAST = 'podcast';
public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];
public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];
public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];
public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];
public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];
public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];
public const int SENT_REPORT_AT_HOURS = 5;
public const string PDF_KEY = 'pdf';
public const string AUDIO_KEY = 'audio';
private const array ALL_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
private const string S3_DIR = 'reports';
private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];
private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly GroupRepository $groupRepository,
private readonly UserRepository $userRepository,
private readonly StageRepository $stageRepository,
private readonly DealStagesService $dealStagesService,
private readonly RecipientsService $recipientsService,
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly Webhook $webhookService,
private readonly BusDispatcher $dispatcher,
private readonly ActivityTypeService $activityTypeService,
private readonly PlaybookCategoryRepository $playbookCategoryRepository,
private readonly AskAnythingPromptService $askAnythingPromptService,
private readonly SearchRepository $activitySearchRepository,
private readonly AskAnythingRepository $askAnythingRepository,
) {
}
public static function getTypes(): array
{
$types = self::TYPES;
return array_map(static function ($type) {
return $type['id'];
}, $types);
}
public static function getCallTypes(): array
{
return array_map(static function ($callType) {
return $callType['id'];
}, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);
}
public static function getFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::FREQUENCIES);
}
// front-facing structure
public function getReportEnabledFieldData(bool $value = false): array
{
return [
'id' => 'report_enabled',
'label' => '',
'inputType' => InputTypeEnum::TOGGLE,
'value' => $value,
];
}
// Organizations = Teams
public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array
{
$options = $this->getTeams();
if ($shortVersion) {
return [
'id' => 'organization',
'label' => 'Organization',
'options' => $options,
];
}
return [
'id' => 'organization',
'label' => 'Organization',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $options,
'value' => $value,
'dependencies' => [
'teams',
'deal_stage_at_call',
'current_deal_stage',
'recipients',
ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,
],
'dependsOn' => [],
];
}
// Teams = Groups
public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array
{
if ($shortVersion) {
return [
'id' => 'teams',
'label' => 'Team',
'options' => $options,
];
}
return [
'id' => 'teams',
'label' => 'Team',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => false,
'placeholder' => 'Select',
'options' => $options,
'value' => $value, // value should be an array of objects {id, name}
'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],
'dependsOn' => [],
];
}
public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array
{
$types = [];
if ($team instanceof Team) {
if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
$types = self::TYPES;
}
if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {
$types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];
}
} else {
$types = self::TYPES;
}
if ($shortVersion) {
return [
'id' => 'report_type',
'label' => 'Report Type',
'options' => $types,
];
}
return [
'id' => 'report_type',
'label' => 'Report Type',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $types,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getFrequencyFieldData(?string $value = null): array
{
return [
'id' => 'frequency',
'label' => 'Frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => self::FREQUENCIES,
'value' => $value,
'dependencies' => ['period'],
'dependsOn' => [],
];
}
public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array
{
return [
'id' => 'period',
'label' => 'Select one-off period',
'inputType' => InputTypeEnum::DATE_RANGE,
'required' => true,
'placeholder' => 'Select',
'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],
'queryParams' => [
'startDate' => 'start_date_period',
'endDate' => 'end_date_period',
],
'dependencies' => [],
'dependsOn' => ['frequency'],
];
}
public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array
{
return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);
}
public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);
}
public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);
}
public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'deal_value',
'label' => 'Deal Value',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_deal_value',
'max' => 'max_deal_value',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array
{
$value = [];
$conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;
$dialerOn && $value[] = self::CALL_TYPE_DIALER;
return [
'id' => 'call_type',
'label' => 'Call Type',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => [
self::CALL_TYPE_CONFERENCE,
self::CALL_TYPE_DIALER,
],
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getMediaTypeFieldData(?AutomatedReport $report = null): array
{
$value = [];
if ($report) {
$value = $this->transformMediaTypes($report);
}
return [
'id' => 'media_types',
'label' => 'Export as',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => self::MEDIA_TYPE_OBJECTS,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'call_duration',
'label' => 'Call Duration',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_call_duration',
'max' => 'max_call_duration',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getRecipientsFieldData(?Team $team = null, array $value = []): array
{
return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);
}
public function getJiminnyRecipientsFieldData(array $value = []): array
{
return $this->recipientsService->getJiminnyRecipientsFieldData($value);
}
public function getAdditionalPromptInputFieldData(?string $value = null): array
{
return [
'id' => 'additional_prompt_input',
'label' => 'Special requirements',
'inputType' => InputTypeEnum::TEXTAREA,
'required' => false,
'placeholder' => 'What should be the focus of the report?',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCustomReportNameFieldData(?string $value = null): array
{
return [
'id' => 'custom_name',
'label' => 'Custom report name',
'inputType' => InputTypeEnum::TEXT,
'required' => false,
'placeholder' => 'Enter custom name',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
// data providers
public function getTeams(): array
{
$teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);
$teamData = [];
foreach ($teams as $team) {
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
continue;
}
$teamData[] = $this->transformTeam($team);
}
return $teamData;
}
public function getTeamGroups(string $teamUuid): array
{
$data = [];
$team = $this->getTeam($teamUuid);
if ($team !== null) {
$groups = $team->groups()->get();
foreach ($groups as $group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
];
}
}
return $data;
}
public function getTeamsGroupsOptions(array $filterTeamUuids = []): array
{
$data = [];
$teams = $this->getTeams();
foreach ($teams as $team) {
if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {
continue;
}
$data[] = [
'label' => $team['name'],
'groups' => $this->getTeamGroups($team['id']),
];
}
return $data;
}
public function getTeam(string $teamUuid): ?Team
{
return $this->teamRepository->idOrUuid($teamUuid);
}
public function getTeamById(int $teamId): ?Team
{
return $this->teamRepository->find($teamId);
}
public function getGroupsUuids(AutomatedReport $report): array
{
$uuids = [];
$reportGroups = $report->getGroups();
foreach ($reportGroups as $groupId) {
if ($group = $this->groupRepository->find($groupId)) {
$uuids[] = $group->getUuid();
}
}
return $uuids;
}
public function getPlaybookCategoriesUuids(AutomatedReport $report): array
{
$uuids = [];
$playbookCategories = $report->getPlaybookCategories();
foreach ($playbookCategories as $id) {
if ($category = $this->playbookCategoryRepository->find($id)) {
$uuids[] = $category->getUuid();
}
}
return $uuids;
}
public function getDealAtCallStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getDealAtCallStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getCurrentDealStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getCurrentDealStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getRecipients());
}
public function getJiminnyUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function extractUserUuids(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => $user->getUuid())
->values()
->all();
}
// get mail data
public function getRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getRecipients());
}
/**
* @return array<UserContract>
*/
public function getRecipientUserObjects(AutomatedReport $report): array
{
$userIds = $report->getRecipients()['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->values()
->all();
}
private function getJiminnyRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function buildRecipientUsers(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => [
'email' => $user->getEmailAddress(),
'name' => $user->getName(),
'timezone' => $user->getTimezone()->getName(),
])
->values()
->all();
}
public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array
{
if ($report->isAskJiminnyReport()) {
$recipients = $this->resolveAskJiminnyRecipients($report);
} else {
$recipients = $this->getRecipientUsers($report);
if ($includeJiminny) {
$recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));
}
}
$emails = [];
return array_values(array_filter(
$recipients,
static function ($recipient) use (&$emails) {
if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {
return false;
}
$emails[] = $recipient['email'];
return true;
}
));
}
private function resolveAskJiminnyRecipients(AutomatedReport $report): array
{
$recipients = [];
$creator = $report->getCreator();
if ($creator !== null) {
$recipients[] = [
'email' => $creator->getEmailAddress(),
'name' => $creator->getName(),
'timezone' => $creator->getTimezone()->getName(),
];
}
return array_merge(
$recipients,
$this->buildRecipientUsers($report->getRecipients()),
$this->getGroupRecipientUsers($report),
);
}
private function getGroupRecipientUsers(AutomatedReport $report): array
{
$users = [];
foreach ($report->getGroups() as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group === null) {
continue;
}
foreach ($group->getMembers() as $member) {
$users[] = [
'email' => $member->getEmailAddress(),
'name' => $member->getName(),
'timezone' => $member->getTimezone()->getName(),
];
}
}
return $users;
}
public function getReportTypeName(AutomatedReportResult $report): string
{
$type = $report->getReport()->getType();
$getType = $this->transformReportType($type);
return $getType['name'];
}
public function getReportPeriodName(AutomatedReportResult $report): string
{
$from = $report->getFromDate();
$to = $report->getToDate();
$frequency = $report->getReport()->getFrequency();
if ($from === null || $to === null) {
if (! $report->getReport()->isAskJiminnyReport()) {
$invalidPeriod = $from === null ? 'from' : 'to';
throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);
}
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
}
return $this->formatReportPeriodName($frequency, $from, $to);
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function getReportTeamsName(AutomatedReportResult $report): string
{
$groups = $report->getGroups();
if (empty($groups)) {
return 'All';
}
// Get group names from repository
$groupNames = [];
foreach ($groups as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group) {
$groupNames[] = $group->getName();
}
}
if (count($groupNames) === 1) {
// Single team format
$teamsName = $groupNames[0];
} else {
// Multiple teams format
$teamsName = implode(', ', $groupNames);
}
return $teamsName;
}
public function getReportFileName(AutomatedReportResult $report): string
{
$customName = $report->getReport()->getCustomName();
$periodName = $this->getReportPeriodName($report);
$filenameSuffix = $this->getFilenameSuffix($report);
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$customName} - {$periodName}");
}
$baseName = $this->getReportTypeName($report);
if ($filenameSuffix) {
$baseName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}");
}
public function getReportFileNameWithExtension(AutomatedReportResult $result): string
{
$extension = $this->getMediaTypeMetadata($result)['extension'];
return $this->getReportFileName($result) . '.' . $extension;
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool
{
$recipientIds = array_map('intval', $report->getRecipients()['users'] ?? []);
if (in_array($user->getId(), $recipientIds, true)) {
return true;
}
if ($report->isAskJiminnyReport()) {
$groupId = $user->getGroupId();
if ($groupId !== null && in_array($groupId, $report->getGroups(), true)) {
return true;
}
}
return false;
}
public function transformReportResults(Collection $automatedReportResults): array
{
$data = [];
foreach ($automatedReportResults as $automatedReportResult) {
/** @var AutomatedReportResult $automatedReportResult */
$report = $automatedReportResult->getReport();
$createdBy = $report->getCreator();
$creator = [
'id' => $createdBy?->getUuid(),
'name' => $createdBy?->getName(),
'email' => $createdBy?->getEmailAddress(),
'photoUrl' => $createdBy?->getPhotoUrl(),
];
$data[] = [
'id' => $automatedReportResult->getUuid(),
'name' => $automatedReportResult->getName(),
'frequency' => $this->transformFrequency($report->getFrequency()),
'recipients' => $this->buildRecipients($report),
'report_type' => $this->transformReportType($report->getType()),
'media_type' => $automatedReportResult->getMediaType(),
'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),
'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),
'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),
'creator' => $creator,
];
}
return $data;
}
private function buildRecipients(AutomatedReport $report): array
{
$creatorUuid = $report->getCreator()?->getUuid();
$recipients = array_values(array_filter(
$this->transformRecipients($report->getRecipients()),
static fn (array $recipient): bool => $recipient['id'] !== $creatorUuid,
));
if (! $report->isAskJiminnyReport()) {
return $recipients;
}
return [
...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),
...$recipients,
];
}
public function hasCallTypeConference(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);
}
public function hasCallTypeDialer(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);
}
// transformers
private function transformTeam(Team $team): array
{
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
return [];
}
return [
'id' => $team->getUuid(),
'name' => $team->getName(),
];
}
private function transformReportFullView(AutomatedReport $report): array
{
$base = $this->transformReportBase($report);
return $report->getType() === self::TYPE_ASK_JIMINNY
? $base + $this->transformAskJiminnyFields($report)
: $base + $this->transformStandardReportFields($report);
}
private function transformReportBase(AutomatedReport $report): array
{
return [
'id' => $report->getUuid(),
'organization' => $this->transformOrganization(team: $report->getTeam()),
'report_type' => $this->transformReportType($report->getType()),
'frequency' => $this->transformFrequency($report->getFrequency()),
];
}
private function transformStandardReportFields(AutomatedReport $report): array
{
$team = $report->getTeam();
return [
'report_enabled' => $report->getStatus(),
'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),
'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),
'deal_value_min' => $report->getDealValueMin(),
'deal_value_max' => $report->getDealValueMax(),
'call_types' => $this->transformCallType($report->getCallTypes()),
'media_types' => $this->transformMediaTypes($report),
'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),
'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),
'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),
'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),
'recipients' => $this->transformRecipients($report->getRecipients()),
'created_by' => $this->transformCreator($report->getCreator()),
'additional_prompt_input' => $report->getAdditionalPromptInput(),
'custom_name' => $report->getCustomName(),
'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),
'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),
'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),
];
}
private function transformAskJiminnyFields(AutomatedReport $report): array
{
$team = $report->getTeam();
$creatorId = $report->getAttribute('created_by');
$explicitUserIds = array_values(array_filter(
$report->getRecipients()['users'] ?? [],
static fn ($id) => $id !== $creatorId
));
return [
'report_name' => $report->getCustomName(),
'enabled' => $report->getStatus(),
'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),
'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),
'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),
'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),
];
}
private function transformOrganization(?Team $team): array
{
return [
'id' => $team?->getUuid(),
'name' => $team?->getName(),
];
}
private function transformReportType(string $type): array
{
foreach (self::ALL_TYPES as $typeItem) {
if ($typeItem['id'] === $type) {
return $typeItem;
}
}
return [];
}
private function transformCallType(array $types): array
{
$result = [];
$callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];
foreach ($types as $type) {
foreach ($callTypes as $callTypeItem) {
if ($callTypeItem['id'] === $type) {
$result[] = $callTypeItem;
break;
}
}
}
return $result;
}
private function transformMediaTypes(AutomatedReport $report): array
{
$values = [];
foreach ($report->getMediaTypes() as $mediaType) {
if (! in_array($mediaType, self::MEDIA_TYPES, true)) {
continue;
}
$values[] = match ($mediaType) {
self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,
self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,
};
}
return $values;
}
private function transformFrequency(string $frequency): array
{
foreach (self::ALL_FREQUENCIES as $frequencyItem) {
if ($frequencyItem['id'] === $frequency) {
return $frequencyItem;
}
}
return [];
}
public function transformDurationToMinutes(?int $duration): ?int
{
if (! $duration) {
return null;
}
return (int) ($duration / 60);
}
private function transformGroups(?Team $team, array $groupsIds): array
{
if (empty($groupsIds) || ! $team) {
return [];
}
$data = [];
foreach ($groupsIds as $groupId) {
$group = $team->groups()->where('id', $groupId)->first();
if ($group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
'photoUrl' => $group->getPhotoUrl(),
];
}
}
return $data;
}
private function transformStages(?Team $team, array $stagesIds): array
{
if (empty($stagesIds) || ! $team) {
return [];
}
$data = [];
foreach ($stagesIds as $stageId) {
$stage = $team->stages()->where('id', $stageId)->first();
if ($stage) {
$data[] = [
'id' => $stage->getUuid(),
'name' => $stage->getName(),
];
}
}
return $data;
}
private function transformRecipients(array $recipients): array
{
$users = [];
foreach ($recipients['users'] ?? [] as $userId) {
$users[] = $this->transformUser($userId);
}
return $users;
}
private function transformCreator(?User $user): ?array
{
if ($user === null) {
return null;
}
return $this->transformUser($user->getId());
}
private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array
{
if ($prompt === null) {
return null;
}
return [
'id' => $prompt->getUuid(),
'name' => $prompt->getTitle(),
];
}
private function transformSafeSearch(?Search $search): ?array
{
if ($search === null) {
return null;
}
return [
'id' => $search->getUuid(),
'name' => $search->getName(),
];
}
private function transformUser(int $userId): array
{
/* @var ?User $user */
$user = $this->userRepository->find($userId);
return [
'id' => $user?->getUuid(),
'name' => $user?->getName(),
'email' => $user?->getEmailAddress(),
'photoUrl' => $user?->getPhotoUrl(),
];
}
public function create(array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$validatedData['created_by'] = auth()->id();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
public function update(string $uuid, array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$report = $this->automatedReportsRepository->findByUuid($uuid);
if (! $report) {
throw new InvalidArgumentException('Report not found');
}
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
/**
* Create an Ask Jiminny report.
*/
public function createAskJiminnyReport(array $data, User $creator): array
{
$validatedData = $this->validateAskJiminnyReportData($data, $creator);
$validatedData['created_by'] = $creator->getId();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
return $this->transformReportFullView($automatedReport);
}
/**
* Update an Ask Jiminny report.
*/
public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array
{
if (! $report->isAskJiminnyReport()) {
throw new InvalidArgumentException('Report is not an Ask Jiminny report');
}
$validatedData = $this->validateAskJiminnyReportData($data, $user);
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
return $this->transformReportFullView($automatedReport);
}
public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array
{
$this->automatedReportsRepository->update($report, ['status' => $status]);
return $this->transformReportFullView($report->fresh());
}
/**
* Validate and transform data for Ask Jiminny reports.
*/
private function validateAskJiminnyReportData(array $data, User $user): array
{
// Validate name
$name = trim($data['report_name'] ?? '');
if (empty($name)) {
throw new InvalidArgumentException('Report name is required');
}
if (mb_strlen($name) > 50) {
throw new InvalidArgumentException('Report name must be 50 characters or less');
}
// Validate frequency (only daily, weekly, monthly for Ask Jiminny)
$frequency = $data['frequency'] ?? null;
$askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];
if (! in_array($frequency, $askJiminnyFrequencies, true)) {
throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');
}
// Validate expiration date
$expiresAt = $data['expires_on'] ?? null;
if (empty($expiresAt)) {
throw new InvalidArgumentException('Expiration date is required');
}
try {
$expiresAtDate = Carbon::parse($expiresAt);
} catch (InvalidFormatException $e) {
throw new InvalidArgumentException('Expiration date format is invalid');
}
$maxExpiration = Carbon::now()->addYear()->endOfDay();
if ($expiresAtDate->gt($maxExpiration)) {
throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');
}
if ($expiresAtDate->isPast()) {
throw new InvalidArgumentException('Expiration date cannot be in the past');
}
// Validate saved search
$activitySearchId = $data['saved_search'] ?? null;
if (empty($activitySearchId)) {
throw new InvalidArgumentException('Saved search is required');
}
$savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);
if (! $savedSearch) {
throw new InvalidArgumentException('Saved search not found or does not belong to you');
}
// Validate saved prompt
$askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;
if (empty($askAnythingPromptId)) {
throw new InvalidArgumentException('Ask Jiminny prompt is required');
}
$prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);
if (! $prompt) {
throw new InvalidArgumentException('Ask Jiminny prompt not found');
}
// Validate status
$status = $data['enabled'] ?? false;
$recipientUserIds = [$user->getId()];
if (! empty($data['share_users'])) {
$sharedUserIds = $this->validateAndGetUserIdsByTeam(
$user->team,
(array) $data['share_users']
);
$recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);
}
$sharedGroupIds = [];
if (! empty($data['share_teams'])) {
$sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);
}
$recipientUserIds = array_values(array_unique($recipientUserIds));
return [
'team_id' => $user->getTeamId(),
'type' => self::TYPE_ASK_JIMINNY,
'status' => (bool) $status,
'frequency' => $frequency,
'custom_name' => $name,
'activity_search_id' => $savedSearch->getId(),
'ask_anything_prompt_id' => $prompt->getId(),
'expires_at' => $expiresAtDate->toDateString(),
'media_types' => [self::MEDIA_TYPE_PDF],
'call_types' => [],
'recipients' => ['users' => $recipientUserIds],
'groups' => $sharedGroupIds,
];
}
public static function getAskJiminnyFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::ASK_JIMINNY_FREQUENCIES);
}
public function getAskJiminnyReportFilters(User $user): array
{
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
return [
[
'id' => 'prompt',
'label' => 'Prompt',
'options' => $prompts,
],
[
'id' => 'saved_search',
'label' => 'Saved Search',
'options' => $savedSearches,
],
];
}
public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array
{
$team = $user->getTeam();
$userTimezone = $user->getTimezone();
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
$teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [
'id' => $group->getUuid(),
'name' => $group->getName(),
])->values()->all();
$shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];
$sharedTeamsValue = [];
$sharedUsersValue = [];
if ($report) {
$sharedTeamsValue = $this->transformGroups($team, $report->getGroups());
$recipientUserIds = $report->getRecipients()['users'] ?? [];
$creatorId = $report->getAttribute('created_by');
$sharedUserIds = array_values(array_filter(
$recipientUserIds,
static fn ($id) => $id !== $creatorId
));
$sharedUsersValue = collect($sharedUserIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (User $u) => [
'id' => $u->getUuid(),
'name' => $u->getName(),
])
->values()
->all();
}
return [
'fields' => [
[
'id' => 'enabled',
'inputType' => InputTypeEnum::TOGGLE,
'label' => '',
'value' => $report?->getStatus() ?? false,
],
[
'id' => 'report_name',
'inputType' => InputTypeEnum::TEXT,
'label' => 'Name',
'placeholder' => 'Enter name',
'required' => true,
'validation' => ['maxLength' => 50],
'value' => $report?->getCustomName() ?? '',
],
[
'id' => 'frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'label' => 'Frequency',
'required' => true,
'placeholder' => 'Select',
'options' => self::ASK_JIMINNY_FREQUENCIES,
'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,
],
[
'id' => 'expires_on',
'inputType' =>...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71568
|
|
71569
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
102
3
34
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Kiosk\AutomatedReports;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\ActivitySearch\FilterDefinition\InputTypeEnum;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateReportJob;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Throwable;
class AutomatedReportsService
{
public const string TYPE_LOSS_ANALYSIS = 'loss_analysis';
public const string TYPE_ASK_JIMINNY = 'ask_jiminny';
/**
* Standard report types (used by kiosk for existing automated reports).
*/
// @TODO this will add filter, however if we need to control feature by FF we need conditional logic
public const array TYPES = [
['id' => 'exec_summary', 'name' => 'Exec Summary'],
['id' => 'coaching_profiles', 'name' => 'Coaching Profiles'],
['id' => 'product_feedback', 'name' => 'Product Feedback'],
['id' => self::TYPE_LOSS_ANALYSIS, 'name' => 'Loss Analysis'],
// ['id' => 'questions', 'name' => 'Questions'],
// ['id' => 'statistical_quant', 'name' => 'Statistical Quantitative'],
];
public const array ALL_TYPES = [
...self::TYPES,
['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'],
];
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
/**
* Frequencies for standard (non-Ask Jiminny) reports.
*/
public const array FREQUENCIES = [
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
/**
* Frequencies for Ask Jiminny reports.
*/
public const array ASK_JIMINNY_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
];
public const string MEDIA_TYPE_PDF = 'pdf';
public const string MEDIA_TYPE_PODCAST = 'podcast';
public const array MEDIA_TYPES = [self::MEDIA_TYPE_PDF, self::MEDIA_TYPE_PODCAST];
public const array MEDIA_TYPE_OBJECT_PDF = ['id' => self::MEDIA_TYPE_PDF, 'name' => 'PDF'];
public const array MEDIA_TYPE_OBJECT_PODCAST = ['id' => self::MEDIA_TYPE_PODCAST, 'name' => 'Podcast'];
public const array MEDIA_TYPE_OBJECTS = [self::MEDIA_TYPE_OBJECT_PDF, self::MEDIA_TYPE_OBJECT_PODCAST];
public const array CALL_TYPE_CONFERENCE = ['id' => 'conference', 'name' => 'Conference'];
public const array CALL_TYPE_DIALER = ['id' => 'dialer', 'name' => 'Dialer'];
public const int SENT_REPORT_AT_HOURS = 5;
public const string PDF_KEY = 'pdf';
public const string AUDIO_KEY = 'audio';
private const array ALL_FREQUENCIES = [
['id' => self::FREQUENCY_DAILY, 'name' => 'Daily'],
['id' => self::FREQUENCY_WEEKLY, 'name' => 'Weekly'],
['id' => self::FREQUENCY_MONTHLY, 'name' => 'Monthly'],
['id' => self::FREQUENCY_QUARTERLY, 'name' => 'Quarterly'],
['id' => self::FREQUENCY_ONE_OFF, 'name' => 'One-off'],
];
private const string S3_DIR = 'reports';
private const array FILE_EXTENSIONS_VARIANTS = ['html', 'MD', 'pdf'];
private const array FILE_PODCAST_EXTENSIONS_VARIANTS = ['json', 'mp3', 'ssml'];
public function __construct(
private readonly TeamRepository $teamRepository,
private readonly GroupRepository $groupRepository,
private readonly UserRepository $userRepository,
private readonly StageRepository $stageRepository,
private readonly DealStagesService $dealStagesService,
private readonly RecipientsService $recipientsService,
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly Webhook $webhookService,
private readonly BusDispatcher $dispatcher,
private readonly ActivityTypeService $activityTypeService,
private readonly PlaybookCategoryRepository $playbookCategoryRepository,
private readonly AskAnythingPromptService $askAnythingPromptService,
private readonly SearchRepository $activitySearchRepository,
private readonly AskAnythingRepository $askAnythingRepository,
) {
}
public static function getTypes(): array
{
$types = self::TYPES;
return array_map(static function ($type) {
return $type['id'];
}, $types);
}
public static function getCallTypes(): array
{
return array_map(static function ($callType) {
return $callType['id'];
}, [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER]);
}
public static function getFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::FREQUENCIES);
}
// front-facing structure
public function getReportEnabledFieldData(bool $value = false): array
{
return [
'id' => 'report_enabled',
'label' => '',
'inputType' => InputTypeEnum::TOGGLE,
'value' => $value,
];
}
// Organizations = Teams
public function getOrganizationFieldData(?string $value = null, bool $shortVersion = false): array
{
$options = $this->getTeams();
if ($shortVersion) {
return [
'id' => 'organization',
'label' => 'Organization',
'options' => $options,
];
}
return [
'id' => 'organization',
'label' => 'Organization',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $options,
'value' => $value,
'dependencies' => [
'teams',
'deal_stage_at_call',
'current_deal_stage',
'recipients',
ActivityTypeService::PLAYBOOK_CATEGORIES_KEY,
],
'dependsOn' => [],
];
}
// Teams = Groups
public function getTeamFieldData(array $options = [], array $value = [], bool $shortVersion = false): array
{
if ($shortVersion) {
return [
'id' => 'teams',
'label' => 'Team',
'options' => $options,
];
}
return [
'id' => 'teams',
'label' => 'Team',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => false,
'placeholder' => 'Select',
'options' => $options,
'value' => $value, // value should be an array of objects {id, name}
'dependencies' => [ActivityTypeService::PLAYBOOK_CATEGORIES_KEY],
'dependsOn' => [],
];
}
public function getReportTypeFieldData(?string $value = null, bool $shortVersion = false, ?Team $team = null): array
{
$types = [];
if ($team instanceof Team) {
if ($team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
$types = self::TYPES;
}
if ($team->hasFeature(FeatureEnum::ASK_JIMINNY_REPORTS)) {
$types[] = ['id' => self::TYPE_ASK_JIMINNY, 'name' => 'Ask Jiminny'];
}
} else {
$types = self::TYPES;
}
if ($shortVersion) {
return [
'id' => 'report_type',
'label' => 'Report Type',
'options' => $types,
];
}
return [
'id' => 'report_type',
'label' => 'Report Type',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => $types,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getFrequencyFieldData(?string $value = null): array
{
return [
'id' => 'frequency',
'label' => 'Frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'required' => true,
'placeholder' => 'Select',
'options' => self::FREQUENCIES,
'value' => $value,
'dependencies' => ['period'],
'dependsOn' => [],
];
}
public function getPeriodFieldData(?string $valueStartDate = null, ?string $valueEndDate = null): array
{
return [
'id' => 'period',
'label' => 'Select one-off period',
'inputType' => InputTypeEnum::DATE_RANGE,
'required' => true,
'placeholder' => 'Select',
'value' => ['startDate' => $valueStartDate, 'endDate' => $valueEndDate],
'queryParams' => [
'startDate' => 'start_date_period',
'endDate' => 'end_date_period',
],
'dependencies' => [],
'dependsOn' => ['frequency'],
];
}
public function getActivityTypesFieldData(?Team $team = null, array $value = [], array $teamsFilter = []): array
{
return $this->activityTypeService->getActivityTypeFieldData(team: $team, value: $value, groupIds: $teamsFilter);
}
public function getDealStageAtCallFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getDealStageAtCallFieldData(team: $team, value: $value);
}
public function getCurrentDealStageFieldData(?Team $team = null, array $value = []): array
{
return $this->dealStagesService->getCurrentDealStageFieldData(team: $team, value: $value);
}
public function getDealValueFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'deal_value',
'label' => 'Deal Value',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_deal_value',
'max' => 'max_deal_value',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallTypeFieldData(bool $conferenceOn = false, bool $dialerOn = false): array
{
$value = [];
$conferenceOn && $value[] = self::CALL_TYPE_CONFERENCE;
$dialerOn && $value[] = self::CALL_TYPE_DIALER;
return [
'id' => 'call_type',
'label' => 'Call Type',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => [
self::CALL_TYPE_CONFERENCE,
self::CALL_TYPE_DIALER,
],
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getMediaTypeFieldData(?AutomatedReport $report = null): array
{
$value = [];
if ($report) {
$value = $this->transformMediaTypes($report);
}
return [
'id' => 'media_types',
'label' => 'Export as',
'inputType' => InputTypeEnum::DROPDOWN_MULTIPLE,
'required' => true,
'options' => self::MEDIA_TYPE_OBJECTS,
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCallDurationFieldData(?int $valueMin = null, ?int $valueMax = null): array
{
return [
'id' => 'call_duration',
'label' => 'Call Duration',
'inputType' => InputTypeEnum::INTEGER_RANGE,
'required' => false,
'value' => ['min' => $valueMin, 'max' => $valueMax],
'queryParams' => [
'min' => 'min_call_duration',
'max' => 'max_call_duration',
],
'dependencies' => [],
'dependsOn' => [],
];
}
public function getRecipientsFieldData(?Team $team = null, array $value = []): array
{
return $this->recipientsService->getRecipientsFieldData(team: $team, value: $value);
}
public function getJiminnyRecipientsFieldData(array $value = []): array
{
return $this->recipientsService->getJiminnyRecipientsFieldData($value);
}
public function getAdditionalPromptInputFieldData(?string $value = null): array
{
return [
'id' => 'additional_prompt_input',
'label' => 'Special requirements',
'inputType' => InputTypeEnum::TEXTAREA,
'required' => false,
'placeholder' => 'What should be the focus of the report?',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
public function getCustomReportNameFieldData(?string $value = null): array
{
return [
'id' => 'custom_name',
'label' => 'Custom report name',
'inputType' => InputTypeEnum::TEXT,
'required' => false,
'placeholder' => 'Enter custom name',
'value' => $value,
'dependencies' => [],
'dependsOn' => [],
];
}
// data providers
public function getTeams(): array
{
$teams = $this->teamRepository->getTeamsForKiosk(status: Team::STATUS_ACTIVE);
$teamData = [];
foreach ($teams as $team) {
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
continue;
}
$teamData[] = $this->transformTeam($team);
}
return $teamData;
}
public function getTeamGroups(string $teamUuid): array
{
$data = [];
$team = $this->getTeam($teamUuid);
if ($team !== null) {
$groups = $team->groups()->get();
foreach ($groups as $group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
];
}
}
return $data;
}
public function getTeamsGroupsOptions(array $filterTeamUuids = []): array
{
$data = [];
$teams = $this->getTeams();
foreach ($teams as $team) {
if (! empty($filterTeamUuids) && ! in_array($team['id'], $filterTeamUuids, true)) {
continue;
}
$data[] = [
'label' => $team['name'],
'groups' => $this->getTeamGroups($team['id']),
];
}
return $data;
}
public function getTeam(string $teamUuid): ?Team
{
return $this->teamRepository->idOrUuid($teamUuid);
}
public function getTeamById(int $teamId): ?Team
{
return $this->teamRepository->find($teamId);
}
public function getGroupsUuids(AutomatedReport $report): array
{
$uuids = [];
$reportGroups = $report->getGroups();
foreach ($reportGroups as $groupId) {
if ($group = $this->groupRepository->find($groupId)) {
$uuids[] = $group->getUuid();
}
}
return $uuids;
}
public function getPlaybookCategoriesUuids(AutomatedReport $report): array
{
$uuids = [];
$playbookCategories = $report->getPlaybookCategories();
foreach ($playbookCategories as $id) {
if ($category = $this->playbookCategoryRepository->find($id)) {
$uuids[] = $category->getUuid();
}
}
return $uuids;
}
public function getDealAtCallStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getDealAtCallStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getCurrentDealStagesUuids(AutomatedReport $report): array
{
$uuids = [];
$reportStages = $report->getCurrentDealStages();
foreach ($reportStages as $id) {
if ($stage = $this->stageRepository->find($id)) {
$uuids[] = $stage->getUuid();
}
}
return $uuids;
}
public function getUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getRecipients());
}
public function getJiminnyUsersUuids(AutomatedReport $report): array
{
return $this->extractUserUuids($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function extractUserUuids(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => $user->getUuid())
->values()
->all();
}
// get mail data
public function getRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getRecipients());
}
/**
* @return array<UserContract>
*/
public function getRecipientUserObjects(AutomatedReport $report): array
{
$userIds = $report->getRecipients()['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->values()
->all();
}
private function getJiminnyRecipientUsers(AutomatedReport $report): array
{
return $this->buildRecipientUsers($report->getJiminnyRecipients());
}
/**
* @param array<string, mixed> $recipients
*/
private function buildRecipientUsers(array $recipients): array
{
$userIds = $recipients['users'] ?? [];
return collect($userIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (UserContract $user) => [
'email' => $user->getEmailAddress(),
'name' => $user->getName(),
'timezone' => $user->getTimezone()->getName(),
])
->values()
->all();
}
public function getValidRecipientUsers(AutomatedReport $report, bool $includeJiminny = false): array
{
if ($report->isAskJiminnyReport()) {
$recipients = $this->resolveAskJiminnyRecipients($report);
} else {
$recipients = $this->getRecipientUsers($report);
if ($includeJiminny) {
$recipients = array_merge($recipients, $this->getJiminnyRecipientUsers($report));
}
}
$emails = [];
return array_values(array_filter(
$recipients,
static function ($recipient) use (&$emails) {
if (empty($recipient['email']) || in_array($recipient['email'], $emails, true)) {
return false;
}
$emails[] = $recipient['email'];
return true;
}
));
}
private function resolveAskJiminnyRecipients(AutomatedReport $report): array
{
$recipients = [];
$creator = $report->getCreator();
if ($creator !== null) {
$recipients[] = [
'email' => $creator->getEmailAddress(),
'name' => $creator->getName(),
'timezone' => $creator->getTimezone()->getName(),
];
}
return array_merge(
$recipients,
$this->buildRecipientUsers($report->getRecipients()),
$this->getGroupRecipientUsers($report),
);
}
private function getGroupRecipientUsers(AutomatedReport $report): array
{
$users = [];
foreach ($report->getGroups() as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group === null) {
continue;
}
foreach ($group->getMembers() as $member) {
$users[] = [
'email' => $member->getEmailAddress(),
'name' => $member->getName(),
'timezone' => $member->getTimezone()->getName(),
];
}
}
return $users;
}
public function getReportTypeName(AutomatedReportResult $report): string
{
$type = $report->getReport()->getType();
$getType = $this->transformReportType($type);
return $getType['name'];
}
public function getReportPeriodName(AutomatedReportResult $report): string
{
$from = $report->getFromDate();
$to = $report->getToDate();
$frequency = $report->getReport()->getFrequency();
if ($from === null || $to === null) {
if (! $report->getReport()->isAskJiminnyReport()) {
$invalidPeriod = $from === null ? 'from' : 'to';
throw new ApplicationException('Report period is invalid: ' . $invalidPeriod);
}
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
}
return $this->formatReportPeriodName($frequency, $from, $to);
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function getReportTeamsName(AutomatedReportResult $report): string
{
$groups = $report->getGroups();
if (empty($groups)) {
return 'All';
}
// Get group names from repository
$groupNames = [];
foreach ($groups as $groupId) {
$group = $this->groupRepository->find($groupId);
if ($group) {
$groupNames[] = $group->getName();
}
}
if (count($groupNames) === 1) {
// Single team format
$teamsName = $groupNames[0];
} else {
// Multiple teams format
$teamsName = implode(', ', $groupNames);
}
return $teamsName;
}
public function getReportFileName(AutomatedReportResult $report): string
{
$customName = $report->getReport()->getCustomName();
$periodName = $this->getReportPeriodName($report);
$filenameSuffix = $this->getFilenameSuffix($report);
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$customName} - {$periodName}");
}
$baseName = $this->getReportTypeName($report);
if ($filenameSuffix) {
$baseName .= " {$filenameSuffix}";
}
return $this->sanitizeFileName("{$baseName} - {$periodName} - {$this->getReportTeamsName($report)}");
}
public function getReportFileNameWithExtension(AutomatedReportResult $result): string
{
$extension = $this->getMediaTypeMetadata($result)['extension'];
return $this->getReportFileName($result) . '.' . $extension;
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
public function isUserRecipientOfReport(User $user, AutomatedReport $report): bool
{
$recipientIds = array_map('intval', $report->getRecipients()['users'] ?? []);
if (in_array($user->getId(), $recipientIds, true)) {
return true;
}
if ($report->isAskJiminnyReport()) {
$groupId = $user->getGroupId();
if ($groupId !== null && in_array($groupId, $report->getGroups(), true)) {
return true;
}
}
return false;
}
public function transformReportResults(Collection $automatedReportResults): array
{
$data = [];
foreach ($automatedReportResults as $automatedReportResult) {
/** @var AutomatedReportResult $automatedReportResult */
$report = $automatedReportResult->getReport();
$createdBy = $report->getCreator();
$creator = [
'id' => $createdBy?->getUuid(),
'name' => $createdBy?->getName(),
'email' => $createdBy?->getEmailAddress(),
'photoUrl' => $createdBy?->getPhotoUrl(),
];
$data[] = [
'id' => $automatedReportResult->getUuid(),
'name' => $automatedReportResult->getName(),
'frequency' => $this->transformFrequency($report->getFrequency()),
'recipients' => $this->buildRecipients($report),
'report_type' => $this->transformReportType($report->getType()),
'media_type' => $automatedReportResult->getMediaType(),
'downloadUrl' => $this->generateReportResultDownloadUrl($automatedReportResult),
'viewUrl' => $this->generateReportResultViewUrl($automatedReportResult),
'generated_at' => $automatedReportResult->getGeneratedAt()?->toIso8601String(),
'creator' => $creator,
];
}
return $data;
}
private function buildRecipients(AutomatedReport $report): array
{
$creatorUuid = $report->getCreator()?->getUuid();
$recipients = array_values(array_filter(
$this->transformRecipients($report->getRecipients()),
static fn (array $recipient): bool => $recipient['id'] !== $creatorUuid,
));
if (! $report->isAskJiminnyReport()) {
return $recipients;
}
return [
...array_values($this->transformGroups(team: $report->getTeam(), groupsIds: $report->getGroups())),
...$recipients,
];
}
public function hasCallTypeConference(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_CONFERENCE['id'], $report->getCallTypes(), true);
}
public function hasCallTypeDialer(AutomatedReport $report): bool
{
return in_array(self::CALL_TYPE_DIALER['id'], $report->getCallTypes(), true);
}
// transformers
private function transformTeam(Team $team): array
{
if (! $team->hasFeature(FeatureEnum::AUTOMATED_REPORTS)) {
return [];
}
return [
'id' => $team->getUuid(),
'name' => $team->getName(),
];
}
private function transformReportFullView(AutomatedReport $report): array
{
$base = $this->transformReportBase($report);
return $report->getType() === self::TYPE_ASK_JIMINNY
? $base + $this->transformAskJiminnyFields($report)
: $base + $this->transformStandardReportFields($report);
}
private function transformReportBase(AutomatedReport $report): array
{
return [
'id' => $report->getUuid(),
'organization' => $this->transformOrganization(team: $report->getTeam()),
'report_type' => $this->transformReportType($report->getType()),
'frequency' => $this->transformFrequency($report->getFrequency()),
];
}
private function transformStandardReportFields(AutomatedReport $report): array
{
$team = $report->getTeam();
return [
'report_enabled' => $report->getStatus(),
'start_date_period' => $report->getFrom()?->format('Y-m-d H:i:s'),
'end_date_period' => $report->getTo()?->format('Y-m-d H:i:s'),
'deal_value_min' => $report->getDealValueMin(),
'deal_value_max' => $report->getDealValueMax(),
'call_types' => $this->transformCallType($report->getCallTypes()),
'media_types' => $this->transformMediaTypes($report),
'call_duration_min' => $this->transformDurationToMinutes($report->getCallDurationMin()),
'call_duration_max' => $this->transformDurationToMinutes($report->getCallDurationMax()),
'teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'deal_at_call_stages' => $this->transformStages(team: $team, stagesIds: $report->getDealAtCallStages()),
'current_deal_stages' => $this->transformStages(team: $team, stagesIds: $report->getCurrentDealStages()),
'recipients' => $this->transformRecipients($report->getRecipients()),
'created_by' => $this->transformCreator($report->getCreator()),
'additional_prompt_input' => $report->getAdditionalPromptInput(),
'custom_name' => $report->getCustomName(),
'created_at' => $report->getCreatedAt()->format('Y-m-d H:i:s'),
'updated_at' => $report->getUpdatedAt()->format('Y-m-d H:i:s'),
'deleted_at' => $report->getDeletedAt()?->format('Y-m-d H:i:s'),
];
}
private function transformAskJiminnyFields(AutomatedReport $report): array
{
$team = $report->getTeam();
$creatorId = $report->getAttribute('created_by');
$explicitUserIds = array_values(array_filter(
$report->getRecipients()['users'] ?? [],
static fn ($id) => $id !== $creatorId
));
return [
'report_name' => $report->getCustomName(),
'enabled' => $report->getStatus(),
'share_teams' => $this->transformGroups(team: $team, groupsIds: $report->getGroups()),
'share_users' => $this->transformRecipients(['users' => $explicitUserIds]),
'saved_search' => $this->transformSafeSearch($report->getSavedSearch()),
'ask_jiminny_prompt' => $this->transformAskJiminnyPrompt($report->getAskAnythingPrompt()),
'expires_on' => $report->getExpiresAt()?->format('Y-m-d'),
];
}
private function transformOrganization(?Team $team): array
{
return [
'id' => $team?->getUuid(),
'name' => $team?->getName(),
];
}
private function transformReportType(string $type): array
{
foreach (self::ALL_TYPES as $typeItem) {
if ($typeItem['id'] === $type) {
return $typeItem;
}
}
return [];
}
private function transformCallType(array $types): array
{
$result = [];
$callTypes = [self::CALL_TYPE_CONFERENCE, self::CALL_TYPE_DIALER];
foreach ($types as $type) {
foreach ($callTypes as $callTypeItem) {
if ($callTypeItem['id'] === $type) {
$result[] = $callTypeItem;
break;
}
}
}
return $result;
}
private function transformMediaTypes(AutomatedReport $report): array
{
$values = [];
foreach ($report->getMediaTypes() as $mediaType) {
if (! in_array($mediaType, self::MEDIA_TYPES, true)) {
continue;
}
$values[] = match ($mediaType) {
self::MEDIA_TYPE_PDF => self::MEDIA_TYPE_OBJECT_PDF,
self::MEDIA_TYPE_PODCAST => self::MEDIA_TYPE_OBJECT_PODCAST,
};
}
return $values;
}
private function transformFrequency(string $frequency): array
{
foreach (self::ALL_FREQUENCIES as $frequencyItem) {
if ($frequencyItem['id'] === $frequency) {
return $frequencyItem;
}
}
return [];
}
public function transformDurationToMinutes(?int $duration): ?int
{
if (! $duration) {
return null;
}
return (int) ($duration / 60);
}
private function transformGroups(?Team $team, array $groupsIds): array
{
if (empty($groupsIds) || ! $team) {
return [];
}
$data = [];
foreach ($groupsIds as $groupId) {
$group = $team->groups()->where('id', $groupId)->first();
if ($group) {
$data[] = [
'id' => $group->getUuid(),
'name' => $group->getName(),
'photoUrl' => $group->getPhotoUrl(),
];
}
}
return $data;
}
private function transformStages(?Team $team, array $stagesIds): array
{
if (empty($stagesIds) || ! $team) {
return [];
}
$data = [];
foreach ($stagesIds as $stageId) {
$stage = $team->stages()->where('id', $stageId)->first();
if ($stage) {
$data[] = [
'id' => $stage->getUuid(),
'name' => $stage->getName(),
];
}
}
return $data;
}
private function transformRecipients(array $recipients): array
{
$users = [];
foreach ($recipients['users'] ?? [] as $userId) {
$users[] = $this->transformUser($userId);
}
return $users;
}
private function transformCreator(?User $user): ?array
{
if ($user === null) {
return null;
}
return $this->transformUser($user->getId());
}
private function transformAskJiminnyPrompt(?AskAnythingPrompt $prompt): ?array
{
if ($prompt === null) {
return null;
}
return [
'id' => $prompt->getUuid(),
'name' => $prompt->getTitle(),
];
}
private function transformSafeSearch(?Search $search): ?array
{
if ($search === null) {
return null;
}
return [
'id' => $search->getUuid(),
'name' => $search->getName(),
];
}
private function transformUser(int $userId): array
{
/* @var ?User $user */
$user = $this->userRepository->find($userId);
return [
'id' => $user?->getUuid(),
'name' => $user?->getName(),
'email' => $user?->getEmailAddress(),
'photoUrl' => $user?->getPhotoUrl(),
];
}
public function create(array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$validatedData['created_by'] = auth()->id();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
public function update(string $uuid, array $data): array
{
$validatedData = $this->validateAndTransformData($data);
$report = $this->automatedReportsRepository->findByUuid($uuid);
if (! $report) {
throw new InvalidArgumentException('Report not found');
}
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
$this->generateOneOffReport($automatedReport);
return $this->transformReportFullView($automatedReport);
}
/**
* Create an Ask Jiminny report.
*/
public function createAskJiminnyReport(array $data, User $creator): array
{
$validatedData = $this->validateAskJiminnyReportData($data, $creator);
$validatedData['created_by'] = $creator->getId();
$automatedReport = $this->automatedReportsRepository->create($validatedData);
return $this->transformReportFullView($automatedReport);
}
/**
* Update an Ask Jiminny report.
*/
public function updateAskJiminnyReport(AutomatedReport $report, array $data, User $user): array
{
if (! $report->isAskJiminnyReport()) {
throw new InvalidArgumentException('Report is not an Ask Jiminny report');
}
$validatedData = $this->validateAskJiminnyReportData($data, $user);
$oldCustomName = $report->getCustomName();
$automatedReport = $this->automatedReportsRepository->update($report, $validatedData);
if ($oldCustomName !== $automatedReport->getCustomName()) {
$this->updateResultNames($automatedReport);
}
return $this->transformReportFullView($automatedReport);
}
public function updateAskJiminnyReportStatus(AutomatedReport $report, bool $status): array
{
$this->automatedReportsRepository->update($report, ['status' => $status]);
return $this->transformReportFullView($report->fresh());
}
/**
* Validate and transform data for Ask Jiminny reports.
*/
private function validateAskJiminnyReportData(array $data, User $user): array
{
// Validate name
$name = trim($data['report_name'] ?? '');
if (empty($name)) {
throw new InvalidArgumentException('Report name is required');
}
if (mb_strlen($name) > 50) {
throw new InvalidArgumentException('Report name must be 50 characters or less');
}
// Validate frequency (only daily, weekly, monthly for Ask Jiminny)
$frequency = $data['frequency'] ?? null;
$askJiminnyFrequencies = [self::FREQUENCY_DAILY, self::FREQUENCY_WEEKLY, self::FREQUENCY_MONTHLY];
if (! in_array($frequency, $askJiminnyFrequencies, true)) {
throw new InvalidArgumentException('Frequency must be daily, weekly, or monthly');
}
// Validate expiration date
$expiresAt = $data['expires_on'] ?? null;
if (empty($expiresAt)) {
throw new InvalidArgumentException('Expiration date is required');
}
try {
$expiresAtDate = Carbon::parse($expiresAt);
} catch (InvalidFormatException $e) {
throw new InvalidArgumentException('Expiration date format is invalid');
}
$maxExpiration = Carbon::now()->addYear()->endOfDay();
if ($expiresAtDate->gt($maxExpiration)) {
throw new InvalidArgumentException('Expiration date cannot be more than 1 year from now');
}
if ($expiresAtDate->isPast()) {
throw new InvalidArgumentException('Expiration date cannot be in the past');
}
// Validate saved search
$activitySearchId = $data['saved_search'] ?? null;
if (empty($activitySearchId)) {
throw new InvalidArgumentException('Saved search is required');
}
$savedSearch = $this->activitySearchRepository->findByUuidAndUser($activitySearchId, $user);
if (! $savedSearch) {
throw new InvalidArgumentException('Saved search not found or does not belong to you');
}
// Validate saved prompt
$askAnythingPromptId = $data['ask_jiminny_prompt'] ?? null;
if (empty($askAnythingPromptId)) {
throw new InvalidArgumentException('Ask Jiminny prompt is required');
}
$prompt = $this->askAnythingRepository->getPromptByUuid($askAnythingPromptId);
if (! $prompt) {
throw new InvalidArgumentException('Ask Jiminny prompt not found');
}
// Validate status
$status = $data['enabled'] ?? false;
$recipientUserIds = [$user->getId()];
if (! empty($data['share_users'])) {
$sharedUserIds = $this->validateAndGetUserIdsByTeam(
$user->team,
(array) $data['share_users']
);
$recipientUserIds = array_merge($recipientUserIds, $sharedUserIds);
}
$sharedGroupIds = [];
if (! empty($data['share_teams'])) {
$sharedGroupIds = $this->validateAndGetGroupIds($user->team, (array) $data['share_teams']);
}
$recipientUserIds = array_values(array_unique($recipientUserIds));
return [
'team_id' => $user->getTeamId(),
'type' => self::TYPE_ASK_JIMINNY,
'status' => (bool) $status,
'frequency' => $frequency,
'custom_name' => $name,
'activity_search_id' => $savedSearch->getId(),
'ask_anything_prompt_id' => $prompt->getId(),
'expires_at' => $expiresAtDate->toDateString(),
'media_types' => [self::MEDIA_TYPE_PDF],
'call_types' => [],
'recipients' => ['users' => $recipientUserIds],
'groups' => $sharedGroupIds,
];
}
public static function getAskJiminnyFrequencies(): array
{
return array_map(static function ($frequency) {
return $frequency['id'];
}, self::ASK_JIMINNY_FREQUENCIES);
}
public function getAskJiminnyReportFilters(User $user): array
{
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
return [
[
'id' => 'prompt',
'label' => 'Prompt',
'options' => $prompts,
],
[
'id' => 'saved_search',
'label' => 'Saved Search',
'options' => $savedSearches,
],
];
}
public function getAskJiminnyReportFormData(User $user, ?AutomatedReport $report = null): array
{
$team = $user->getTeam();
$userTimezone = $user->getTimezone();
$savedSearches = $this->activitySearchRepository->findByUserOrderedByName($user)
->map(fn (Search $search) => [
'id' => $search->getUuid(),
'name' => $search->getName(),
])
->values()->all();
$prompts = collect(
$this->askAnythingPromptService->get($user, AskAnythingPromptTarget::on_demand)
)->map(fn (AskAnythingPromptDto $prompt) => [
'id' => $prompt->id,
'name' => $prompt->title,
])->values()->all();
$teamGroups = $this->groupRepository->getAllByTeam($team)->map(fn ($group) => [
'id' => $group->getUuid(),
'name' => $group->getName(),
])->values()->all();
$shareUsers = $this->recipientsService->getRecipientsFieldData(team: $team)['options'] ?? [];
$sharedTeamsValue = [];
$sharedUsersValue = [];
if ($report) {
$sharedTeamsValue = $this->transformGroups($team, $report->getGroups());
$recipientUserIds = $report->getRecipients()['users'] ?? [];
$creatorId = $report->getAttribute('created_by');
$sharedUserIds = array_values(array_filter(
$recipientUserIds,
static fn ($id) => $id !== $creatorId
));
$sharedUsersValue = collect($sharedUserIds)
->map(fn ($id) => $this->userRepository->find((int) $id))
->filter()
->map(fn (User $u) => [
'id' => $u->getUuid(),
'name' => $u->getName(),
])
->values()
->all();
}
return [
'fields' => [
[
'id' => 'enabled',
'inputType' => InputTypeEnum::TOGGLE,
'label' => '',
'value' => $report?->getStatus() ?? false,
],
[
'id' => 'report_name',
'inputType' => InputTypeEnum::TEXT,
'label' => 'Name',
'placeholder' => 'Enter name',
'required' => true,
'validation' => ['maxLength' => 50],
'value' => $report?->getCustomName() ?? '',
],
[
'id' => 'frequency',
'inputType' => InputTypeEnum::DROPDOWN,
'label' => 'Frequency',
'required' => true,
'placeholder' => 'Select',
'options' => self::ASK_JIMINNY_FREQUENCIES,
'value' => $report ? $this->transformFrequency($report->getFrequency()) : null,
],
[
'id' => 'expires_on',
'inputType' =>...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
71569
|