From bb279359b20704769a38472cdac7d0aaca09eea5 Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Tue, 26 Nov 2024 15:56:01 +0000 Subject: [PATCH 1/2] Update CertificateParticipation and LatexCleaner to proocess certs that time out --- app/CertificateParticipation.php | 77 +++++++++++++------------------- app/Traits/LatexCleaner.php | 9 ++-- 2 files changed, 36 insertions(+), 50 deletions(-) diff --git a/app/CertificateParticipation.php b/app/CertificateParticipation.php index 92e25e2f8..7a207ec51 100644 --- a/app/CertificateParticipation.php +++ b/app/CertificateParticipation.php @@ -7,7 +7,6 @@ use Carbon\Carbon; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; -use Log; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; @@ -25,7 +24,6 @@ class CertificateParticipation private $personalized_template_name; - //private $event; private $id; private $event_name; @@ -40,42 +38,34 @@ class CertificateParticipation public function __construct($name_for_certificate, $event_name, $event_date) { + ini_set('max_execution_time', 300); // Set max execution time to 300 seconds $this->name_of_certificate_holder = $name_for_certificate; $this->event_name = $event_name; $this->event_date = $event_date; - //if ($this->is_greek_text($this->name_of_certificate_holder)){ $random = Str::random(10); - //} else { - // $random = Str::kebab($this->name_of_certificate_holder) . "-" . Str::random(10); - //} - $this->personalized_template_name = $random.'-'.auth()->id(); - $this->resource_path = resource_path().'/latex'; + $this->personalized_template_name = $random . '-' . auth()->id(); + $this->resource_path = resource_path() . '/latex'; $this->pdflatex = config('codeweek.pdflatex_path'); - $this->id = auth()->id().'-'.$random; - Log::info('User ID '.auth()->id().' generating participation certificate with name: '.$name_for_certificate); + $this->id = auth()->id() . '-' . $random; } public function generate() { - $this->customize_and_save_latex(); $this->run_pdf_creation(); $this->clean_temp_files(); return $this->personalized_template_name; - //$s3path = $this->copy_to_s3(); - - //return $s3path; - } private function clean_temp_files() { - Storage::disk('latex')->delete($this->personalized_template_name.'.aux'); - Storage::disk('latex')->delete($this->personalized_template_name.'.tex'); - Storage::disk('latex')->delete($this->personalized_template_name.'.log'); + $extensions = ['aux', 'tex', 'log']; + foreach ($extensions as $ext) { + Storage::disk('latex')->delete($this->personalized_template_name . '.' . $ext); + } } protected function update_event($s3path) @@ -86,26 +76,17 @@ protected function update_event($s3path) ]); } - /** - * @throws \League\Flysystem\FileNotFoundException - */ protected function copy_to_s3(): string { - $inputStream = Storage::disk('latex')->getDriver()->readStream($this->personalized_template_name.'.pdf'); - $destination = Storage::disk('s3')->path('/certificates/'.$this->id.'.pdf'); + $inputStream = Storage::disk('latex')->getDriver()->readStream($this->personalized_template_name . '.pdf'); + $destination = Storage::disk('s3')->path('/certificates/' . $this->id . '.pdf'); Storage::disk('s3')->put($destination, $inputStream); - return Storage::disk('s3')->url('certificates/'.$this->id.'.pdf'); + return Storage::disk('s3')->url('certificates/' . $this->id . '.pdf'); } - /** - * @return mixed - * - * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException - */ protected function customize_and_save_latex() { - if ($this->is_greek_text($this->event_name) || $this->is_greek_text($this->event_date) || $this->is_greek_text($this->name_of_certificate_holder)) { $this->templateName = 'participation_greek.tex'; } @@ -121,11 +102,9 @@ protected function customize_and_save_latex() if ($this->is_greek_text($this->name_of_certificate_holder)) { $this->certificate_holder_name_lang = 'greek'; } - // Log::info($this->templateName); - //open the latex template + $base_template = Storage::disk('latex')->get($this->templateName); - //replace the text in template $template = str_replace('', $this->tex_escape($this->name_of_certificate_holder), $base_template); $template = str_replace('', $this->tex_escape($this->event_name), $template); $template = str_replace('', $this->tex_escape($this->event_date), $template); @@ -134,26 +113,34 @@ protected function customize_and_save_latex() $template = str_replace('', $this->tex_escape($this->event_name_lang), $template); $template = str_replace('', $this->tex_escape($this->event_date_lang), $template); - // dd($template); - - //save it locally - Storage::disk('latex')->put($this->personalized_template_name.'.tex', $template); + Storage::disk('latex')->put($this->personalized_template_name . '.tex', $template); } protected function run_pdf_creation(): void { + if (!file_exists($this->pdflatex)) { + throw new \RuntimeException("pdflatex binary not found at path: {$this->pdflatex}"); + } - //call the pdflatex command - $command = $this->pdflatex.' -interaction=nonstopmode -output-directory '.$this->resource_path.' '.$this->resource_path.'/'.$this->personalized_template_name.'.tex'; + $command = escapeshellcmd($this->pdflatex) . ' -interaction=nonstopmode -output-directory ' + . escapeshellarg($this->resource_path) . ' ' + . escapeshellarg($this->resource_path . '/' . $this->personalized_template_name . '.tex'); - $cwd = $this->resource_path; - $process = Process::fromShellCommandline($command, $cwd); - //$process = new Process($command, $cwd); - $process->run(); + $process = Process::fromShellCommandline($command, $this->resource_path); + $process->setTimeout(600); // Set 600 seconds timeout + $this->execute_process($process); - // executes after the command finishes - if (! $process->isSuccessful()) { + if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } } + + private function execute_process(Process $process): void + { + $process->start(); + + while ($process->isRunning()) { + usleep(400000); // Sleep for 400ms + } + } } diff --git a/app/Traits/LatexCleaner.php b/app/Traits/LatexCleaner.php index 867f64a70..43a5b21a3 100644 --- a/app/Traits/LatexCleaner.php +++ b/app/Traits/LatexCleaner.php @@ -9,8 +9,9 @@ public function tex_escape($string) $map = [ 'ʼ' => "'", // Replace Unicode apostrophe with standard apostrophe 'ə' => '\\textschwa{}', // Handle ə + '"' => "''", // Replace double quotes with two single quotes '#' => '\\#', - '$' => '\$', + '$' => '\\$', '%' => '\\%', '&' => '\\&', '~' => '\\~{}', @@ -21,14 +22,12 @@ public function tex_escape($string) '}' => '\\}', ]; - $string = preg_replace_callback( - "/([\^\%~\\\\#\$%&_\{\}ʼ])/", + return preg_replace_callback( + "/([\#\$%&~_\^\\\\{}ʼ\"])/", function ($matches) use ($map) { return $map[$matches[0]] ?? $matches[0]; }, $string ); - - return $string; } } From 4f6a521ba0756d0be077376030da33711db7c7ff Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Tue, 26 Nov 2024 17:12:08 +0000 Subject: [PATCH 2/2] increased max execution to process large generations of certs --- app/CertificateParticipation.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/CertificateParticipation.php b/app/CertificateParticipation.php index 7a207ec51..f8626f1a4 100644 --- a/app/CertificateParticipation.php +++ b/app/CertificateParticipation.php @@ -38,7 +38,9 @@ class CertificateParticipation public function __construct($name_for_certificate, $event_name, $event_date) { - ini_set('max_execution_time', 300); // Set max execution time to 300 seconds + ini_set('max_execution_time', 600); // Set max execution time to 600 seconds + ini_set('memory_limit', '512M'); // Set memory limit to 512 MB (adjust as needed) + $this->name_of_certificate_holder = $name_for_certificate; $this->event_name = $event_name; $this->event_date = $event_date; @@ -127,7 +129,7 @@ protected function run_pdf_creation(): void . escapeshellarg($this->resource_path . '/' . $this->personalized_template_name . '.tex'); $process = Process::fromShellCommandline($command, $this->resource_path); - $process->setTimeout(600); // Set 600 seconds timeout + $process->setTimeout(600); // Allow up to 600 seconds for execution $this->execute_process($process); if (!$process->isSuccessful()) {