argument('schedule_type'); $mailRules = AlertMailRule::where('module', 'DailyTaskReport') ->where('rule_name', 'DailyTaskReportMail') ->where('schedule_type', $scheduleType) ->get(); if (!$mailRules) { $this->error('Daily Task Report mail rule not found.'); return self::FAILURE; } // Get all PDF files $pdfFiles = collect(Storage::disk('local')->files($directory)) ->filter(function ($file) { return strtolower(pathinfo($file, PATHINFO_EXTENSION)) == 'pdf'; }) ->values(); if ($pdfFiles->isEmpty()) { $this->info('No PDF files found.'); return self::SUCCESS; } $parser = new Parser(); $attachments = []; $rows = []; $sno = 1; $attachments = []; foreach ($pdfFiles as $file) { $fullPath = Storage::disk('local')->path($file); $filename = basename($file, '.pdf'); $attachments[] = [ 'path' => Storage::disk('local')->path($file), 'name' => basename($file), ]; // Read PDF content for name/task/status try { $pdf = $parser->parseFile($fullPath); $text = $pdf->getText(); \Log::info('PDF RAW TEXT: ' . $text); } catch (\Throwable $e) { $text = ''; } $rows[] = [ 'sno' => $sno++, 'name' => $this->extractNameFromFilename($filename), // 'project-name' => $this->extractProjectName($text), 'project-name' => $pdf ? $this->extractProjectName($pdf) : 'Unknown project', 'task' => $this->extractTaskTitle($text), 'status' => $this->extractStatus($text), 'completed' => $this->extractPercentComplete($text), ]; $this->info('Attached: ' . basename($file)); } $toEmails = []; $ccEmails = []; foreach ($mailRules as $mailRule) { // TO email if (!empty($mailRule->email)) { $toEmails[] = trim($mailRule->email); } // CC emails if (!empty($mailRule->cc_emails)) { $ccEmails = array_merge( $ccEmails, array_map( 'trim', explode(',', $mailRule->cc_emails) ) ); } } // Remove duplicate emails $toEmails = array_values(array_unique(array_filter($toEmails))); $ccEmails = array_values(array_unique(array_filter($ccEmails))); try { $mail = Mail::to($toEmails); if (!empty($ccEmails)) { $mail->cc($ccEmails); } $mail->send( new DailyTaskReportMail($attachments, $rows) ); foreach ($pdfFiles as $file) { if (Storage::disk('local')->exists($file)) { Storage::disk('local')->delete($file); $this->info('Deleted: ' . basename($file)); } } $this->info( $pdfFiles->count() . ' PDF(s) mailed and deleted successfully.' ); return self::SUCCESS; } catch (\Throwable $e) { // Mail failed -> DO NOT delete PDFs $this->error('Mail sending failed.'); $this->error($e->getMessage()); return self::FAILURE; } } protected function extractNameFromFilename(string $filename): string { $name = preg_replace('/[-_]?task.*$/i', '', $filename); $name = preg_replace('/[-_]+/', ' ', $name); $name = preg_replace('/(?knownStatuses as $status) { $pos = stripos($title, $status); if ($pos != false) { $title = trim(substr($title, 0, $pos)); break; } } return $title; } return 'Unknown task'; } protected function extractStatus(string $text): string { // Replace non-breaking spaces (common in OpenProject PDF exports) with regular spaces $normalized = str_replace("\xC2\xA0", ' ', $text); $normalized = preg_replace('/\s+/', ' ', $normalized); foreach ($this->knownStatuses as $status) { if (stripos($normalized, $status) !== false) { return ucwords($status); } } return 'Unknown'; } protected function extractPercentComplete(string $text): string { $normalized = str_replace("\xC2\xA0", ' ', $text); $normalized = preg_replace('/[ \t]+/', ' ', $normalized); if (preg_match('/%\s*Complete\s*([0-9]{1,3}\s*%)/i', $normalized, $matches)) { return trim(str_replace(' ', '', $matches[1])); } return '0%'; } // protected function extractProjectName(string $text): string // { // $normalized = str_replace("\xC2\xA0", ' ', $text); // // Footer pattern: \t at the very end of the PDF text // if (preg_match('/\d{1,2}-\d{2}-\d{4}\s*\t\s*(.+?)\s*$/s', $normalized, $matches)) { // return trim($matches[1]); // } // return 'Unknown project'; // } protected function extractProjectName($pdf): string { $pages = $pdf->getPages(); if (empty($pages)) { return 'Unknown project'; } $firstPageText = str_replace("\xC2\xA0", ' ', $pages[0]->getText()); if (preg_match('/\d{1,2}-\d{2}-\d{4}\s*\t\s*(.+?)\s*$/s', $firstPageText, $matches)) { return trim($matches[1]); } return 'Unknown project'; } }