Skip to content

Commit cc92945

Browse files
authored
Merge branch 'new-matrix-uat' into login-register-page-redesign
2 parents d388efd + befa9b1 commit cc92945

File tree

12 files changed

+216
-420
lines changed

12 files changed

+216
-420
lines changed

app/CertificateExcellence.php

Lines changed: 75 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -13,70 +13,74 @@ class CertificateExcellence
1313
private $templateName;
1414

1515
private $name_of_certificate_holder;
16+
1617
private $email_of_certificate_holder;
18+
1719
private $resource_path;
20+
1821
private $pdflatex;
22+
1923
private $personalized_template_name;
24+
25+
private $event;
26+
2027
private $id;
28+
2129
private $edition;
30+
2231
private $number_of_activities;
23-
private $type;
2432

25-
public function __construct($edition, $name_for_certificate, $type = 'excellence', $number_of_activities = 0)
33+
public function __construct($edition, $name_for_certificate, $type, $number_of_activities)
2634
{
35+
2736
$this->edition = $edition;
2837
$this->name_of_certificate_holder = $name_for_certificate;
2938
$this->email_of_certificate_holder = auth()->user()->email ?? '';
30-
$this->personalized_template_name = $edition . '-' . auth()->id();
31-
$this->resource_path = resource_path() . '/latex';
39+
$this->personalized_template_name = $edition.'-'.auth()->id();
40+
$this->resource_path = resource_path().'/latex';
3241
$this->pdflatex = config('codeweek.pdflatex_path');
33-
$this->id = auth()->id() . '-' . str_random(10);
42+
$this->id = auth()->id().'-'.str_random(10);
3443
$this->number_of_activities = $number_of_activities;
35-
$this->type = $type ?: 'excellence';
44+
$this->type = $type ?? 'excellence';
3645

37-
// e.g. "excellence-2025.tex" or "super-organiser-2025.tex"
3846
$this->templateName = "{$this->type}-{$this->edition}.tex";
3947

40-
Log::info('User ID ' . auth()->id() . " generating {$this->type} certificate with name: " . $name_for_certificate);
48+
Log::info('User ID '.auth()->id()." generating {$this->type} certificate with name: ".$name_for_certificate);
4149
}
4250

43-
/**
44-
* Generates the certificate PDF, saves to S3, cleans up temp files.
45-
* Returns the S3 path of the generated PDF.
46-
*/
4751
public function generate()
4852
{
53+
4954
$this->customize_and_save_latex();
5055
$this->run_pdf_creation();
5156
$s3path = $this->copy_to_s3();
5257
$this->clean_temp_files();
5358

5459
return $s3path;
60+
5561
}
5662

57-
/**
58-
* Clean up LaTeX artifacts for the generated file.
59-
*/
6063
private function clean_temp_files()
6164
{
62-
Storage::disk('latex')->delete($this->personalized_template_name . '.aux');
63-
Storage::disk('latex')->delete($this->personalized_template_name . '.tex');
64-
Storage::disk('latex')->delete($this->personalized_template_name . '.pdf');
65-
Storage::disk('latex')->delete($this->personalized_template_name . '.log');
65+
Storage::disk('latex')->delete($this->personalized_template_name.'.aux');
66+
Storage::disk('latex')->delete($this->personalized_template_name.'.tex');
67+
Storage::disk('latex')->delete($this->personalized_template_name.'.pdf');
68+
Storage::disk('latex')->delete($this->personalized_template_name.'.log');
6669
}
6770

68-
/**
69-
* Check for Greek characters in the name.
70-
*/
7171
public function is_greek()
7272
{
73+
7374
$split = preg_split('/[\p{Greek}]/u', $this->name_of_certificate_holder);
74-
return (count($split) > 1);
75+
if (count($split) > 1) {
76+
// Log::info("Detected as Greek: " . $this->name_of_certificate_holder);
77+
return true;
78+
}
79+
80+
return false;
81+
7582
}
7683

77-
/**
78-
* Escape LaTeX special characters in user data.
79-
*/
8084
private function tex_escape($string)
8185
{
8286
$map = [
@@ -92,85 +96,82 @@ private function tex_escape($string)
9296
'}' => '\\}',
9397
];
9498

95-
return preg_replace_callback(
96-
"/([\^\%~\\\\#\$%&_\{\}])/",
99+
return preg_replace_callback("/([\^\%~\\\\#\$%&_\{\}])/",
97100
function ($matches) use ($map) {
98101
foreach ($matches as $match) {
99102
return $map[$match];
100103
}
101-
},
102-
$string
103-
);
104+
}, $string);
105+
}
106+
107+
protected function update_event($s3path)
108+
{
109+
$this->event->update([
110+
'certificate_url' => $s3path,
111+
'certificate_generated_at' => Carbon::now(),
112+
]);
104113
}
105114

106115
/**
107-
* Read the base template from disk, replace placeholders, and save the .tex file.
116+
* @throws \League\Flysystem\FileNotFoundException
117+
*/
118+
protected function copy_to_s3(): string
119+
{
120+
$inputStream = Storage::disk('latex')->getDriver()->readStream($this->personalized_template_name.'.pdf');
121+
$destination = Storage::disk('s3')->path('/certificates/'.$this->id.'.pdf');
122+
Storage::disk('s3')->put($destination, $inputStream);
123+
124+
return Storage::disk('s3')->url('certificates/'.$this->id.'.pdf');
125+
}
126+
127+
/**
128+
* @return mixed
129+
*
130+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
108131
*/
109132
protected function customize_and_save_latex()
110133
{
111-
// If the name is Greek, switch to Greek template if it exists:
112134
if ($this->is_greek()) {
113135
$this->templateName = "{$this->type}_greek-{$this->edition}.tex";
114136
}
115-
116-
Log::info("Using template: {$this->templateName}");
137+
Log::info($this->templateName);
138+
//open the latex template
117139
$base_template = Storage::disk('latex')->get($this->templateName);
118140

119-
// Always replace <CERTIFICATE_HOLDER_NAME> for both excellence & super-organiser
120-
$template = str_replace(
121-
'<CERTIFICATE_HOLDER_NAME>',
122-
$this->tex_escape($this->name_of_certificate_holder),
123-
$base_template
124-
);
141+
//replace the text in template
142+
$template = str_replace('<CERTIFICATE_HOLDER_NAME>', $this->tex_escape($this->name_of_certificate_holder), $base_template);
125143

126-
// If super-organiser, we also replace these:
127-
if ($this->type === 'super-organiser') {
128-
// Possibly also <EDITION> if you want it dynamic
129-
$template = str_replace('<NUMBER_OF_ACTIVITIES>', $this->tex_escape($this->number_of_activities), $template);
130-
$template = str_replace('<CERTIFICATE_DATE>', $this->tex_escape(Carbon::now()->format('d/m/Y')), $template);
131-
// If you added <CERTIFICATE_EMAIL> or <EDITION> placeholders, handle them as well:
144+
if ($this->type == 'super-organiser') {
132145
$template = str_replace('<CERTIFICATE_EMAIL>', $this->tex_escape($this->email_of_certificate_holder), $template);
133-
$template = str_replace('<EDITION>', $this->edition, $template);
146+
$template = str_replace('<CERTIFICATE_DATE>', $this->tex_escape(Carbon::now()->format('d/m/Y')), $template);
147+
$template = str_replace('<NUMBER_OF_ACTIVITIES>', $this->tex_escape($this->number_of_activities), $template);
134148
}
135149

136-
// For excellence type, you can do other placeholder replacements here if needed.
137-
138-
// Save updated .tex
139150
Log::info($template);
140-
Storage::disk('latex')->put($this->personalized_template_name . '.tex', $template);
151+
152+
//save it locally
153+
Storage::disk('latex')->put($this->personalized_template_name.'.tex', $template);
141154
}
142155

143-
/**
144-
* Compile the .tex file with pdflatex.
145-
*/
146156
protected function run_pdf_creation(): void
147157
{
148-
$command = $this->pdflatex . ' -interaction=nonstopmode -output-directory ' .
149-
$this->resource_path . ' ' .
150-
$this->resource_path . '/' . $this->personalized_template_name . '.tex';
151158

152-
Log::info("pdflatex command: $command");
159+
//call the pdflatex command
160+
$command = $this->pdflatex.' -interaction=nonstopmode -output-directory '.$this->resource_path.' '.$this->resource_path.'/'.$this->personalized_template_name.'.tex';
161+
162+
Log::info($command);
163+
153164
$cwd = $this->resource_path;
154165

166+
Log::info($cwd);
167+
168+
// $process = new Process($command, $cwd);
155169
$process = Process::fromShellCommandline($command, $cwd);
156170
$process->run();
157171

158-
if (!$process->isSuccessful()) {
172+
// executes after the command finishes
173+
if (! $process->isSuccessful()) {
159174
throw new ProcessFailedException($process);
160175
}
161176
}
162-
163-
/**
164-
* Copy the resulting PDF to S3, return its S3 URL.
165-
*/
166-
protected function copy_to_s3(): string
167-
{
168-
$pdfFile = $this->personalized_template_name . '.pdf';
169-
$inputStream = Storage::disk('latex')->getDriver()->readStream($pdfFile);
170-
$destination = Storage::disk('s3')->path('/certificates/' . $this->id . '.pdf');
171-
172-
Storage::disk('s3')->put($destination, $inputStream);
173-
174-
return Storage::disk('s3')->url('certificates/' . $this->id . '.pdf');
175-
}
176177
}

app/Console/Commands/Excellence.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@ public function handle(): void
6363
Log::info($ex->getMessage());
6464
}
6565
}
66+
6667
}
6768
}

app/Console/Commands/NotifySuperOrganisers.php

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class NotifySuperOrganisers extends Command
2626

2727
/**
2828
* Create a new command instance.
29+
*
30+
* @return void
2931
*/
3032
public function __construct()
3133
{
@@ -37,50 +39,30 @@ public function __construct()
3739
*/
3840
public function handle(): void
3941
{
42+
4043
$edition = $this->argument('edition');
41-
$this->info("🔹 NotifySuperOrganisers command started for edition: $edition");
4244

43-
// Fetch winners for the given edition
4445
$winners = Excellence::byYear($edition, 'SuperOrganiser');
4546

46-
if ($winners->isEmpty()) {
47-
$this->warn("⚠️ No winners found for edition: $edition");
48-
Log::info("No SuperOrganiser winners found for edition: $edition.");
49-
return;
50-
}
51-
52-
$notifiedCount = 0;
53-
5447
foreach ($winners as $winner) {
48+
5549
$user = $winner->user;
5650

5751
if (! $user) {
58-
$this->warn("⚠️ Skipping winner with missing user data (ID: $winner->id)");
59-
Log::warning("Skipping winner with missing user data. Winner ID: $winner->id");
6052
continue;
6153
}
6254

6355
if ($user->email && $user->receive_emails) {
64-
// Send email notification
6556
Mail::to($user->email)->queue(new \App\Mail\NotifySuperOrganiser($user, $edition));
66-
67-
// Mark as notified
6857
$excellence = $user->superOrganisers->where('edition', '=', $edition)->first();
69-
if ($excellence) {
70-
$excellence->notified_at = Carbon::now();
71-
$excellence->save();
72-
}
7358

74-
$notifiedCount++;
75-
$this->info("✅ Email queued for: {$user->email}");
76-
Log::info("Email queued for SuperOrganiser: {$user->email}, Edition: $edition");
59+
$excellence->notified_at = Carbon::now();
60+
$excellence->save();
61+
7762
} else {
78-
$this->warn("⚠️ Skipping user {$user->id} (No valid email or opted out)");
79-
Log::info("User {$user->id} skipped - No valid email or opted out.");
63+
Log::info($user->id.' has no valid email address');
8064
}
81-
}
8265

83-
$this->info("✅ NotifySuperOrganisers command executed for edition: $edition, Emails sent: $notifiedCount");
84-
Log::info("NotifySuperOrganisers command completed. Edition: $edition, Emails sent: $notifiedCount.");
66+
}
8567
}
8668
}

app/Console/Commands/NotifyWinners.php

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class NotifyWinners extends Command
2626

2727
/**
2828
* Create a new command instance.
29+
*
30+
* @return void
2931
*/
3032
public function __construct()
3133
{
@@ -37,46 +39,27 @@ public function __construct()
3739
*/
3840
public function handle(): void
3941
{
40-
$edition = $this->argument('edition');
4142

42-
$this->info("🔹 NotifyWinners command started for edition: {$edition}");
43+
$edition = $this->argument('edition');
4344

44-
// Get all Excellence rows for this edition that have notified_at=null
4545
$winners = Excellence::byYear($edition);
4646

47-
if ($winners->isEmpty()) {
48-
$this->warn("⚠️ No winners found for edition: {$edition}");
49-
return;
50-
}
51-
52-
$count = 0;
5347
foreach ($winners as $winner) {
48+
5449
$user = $winner->user;
5550

56-
// If no user is associated, we remove this row
5751
if (is_null($user)) {
58-
$this->warn("User is null for winner ID {$winner->id}. Deleting row.");
5952
$winner->delete();
60-
continue;
61-
}
62-
63-
// If user has a valid email and is set to receive emails
64-
if ($user->email && $user->receive_emails === 1) {
53+
} elseif ($user->email && $user->receive_emails === 1) {
6554
Mail::to($user->email)->queue(new \App\Mail\NotifyWinner($user, $edition));
55+
$excellence = $user->excellences->where('edition', '=', $edition)->first();
56+
$excellence->notified_at = Carbon::now();
57+
$excellence->save();
6658

67-
// Mark notified
68-
$winner->notified_at = Carbon::now();
69-
$winner->save();
70-
71-
$count++;
72-
$this->info("✅ Queued email for: {$user->email}");
7359
} else {
74-
// user->email is null or user->receive_emails=0
75-
Log::info("User {$user->id} has no valid email or doesn't receive emails");
76-
$this->warn("User {$user->id} not notified (invalid email or receive_emails=0)");
60+
Log::info($user->id.' has no valid email address');
7761
}
78-
}
7962

80-
$this->info("✅ NotifyWinners command finished for edition {$edition}. Emails sent: {$count}");
63+
}
8164
}
8265
}

0 commit comments

Comments
 (0)