@@ -15,7 +15,8 @@ class ExportCertificatesProof extends Command
1515 {--path= : Output path under storage/app (default: exports/certificates_manifest_[range].csv)}
1616 {--family=both : Which family to export: participations|excellence|both}
1717 {--inclusive=0 : If 1, do not require URL and do not force status=DONE}
18- {--date-field=created_at : Date field to use (created_at|event_date|issued_at if present)} ' ;
18+ {--date-field=created_at : Date field to use (created_at|event_date|issued_at if present)}
19+ {--excellence-type= : Optional filter for Excellence types (comma-separated; e.g. SuperOrganiser,Excellence)} ' ;
1920
2021 protected $ description = 'Export a CSV manifest of issued certificates (links + metadata) for the requested interval ' ;
2122
@@ -27,16 +28,18 @@ public function handle()
2728 if (preg_match ('/^\d{4}-\d{2}-\d{2}$/ ' , $ start )) $ start .= ' 00:00:00 ' ;
2829 if (preg_match ('/^\d{4}-\d{2}-\d{2}$/ ' , $ end )) $ end .= ' 23:59:59 ' ;
2930
30- $ family = strtolower ($ this ->option ('family ' ) ?: 'both ' ); // participations|excellence|both
31- $ inclusive = (int )($ this ->option ('inclusive ' ) ?: 0 ) === 1 ;
32- $ datePref = strtolower ($ this ->option ('date-field ' ) ?: 'created_at ' ); // created_at|event_date|issued_at
31+ $ family = strtolower ($ this ->option ('family ' ) ?: 'both ' ); // participations|excellence|both
32+ $ inclusive = (int )($ this ->option ('inclusive ' ) ?: 0 ) === 1 ;
33+ $ datePref = strtolower ($ this ->option ('date-field ' ) ?: 'created_at ' ); // created_at|event_date|issued_at
34+ $ exTypeFilter = $ this ->parseExcellenceTypeFilter ($ this ->option ('excellence-type ' ));
3335
3436 $ defaultPath = 'exports/certificates_manifest_ '
3537 . str_replace ([': ' , ' ' ], ['_ ' , '_ ' ], $ start )
3638 . '_to_ '
3739 . str_replace ([': ' , ' ' ], ['_ ' , '_ ' ], $ end )
3840 . ($ inclusive ? '_inclusive ' : '' )
3941 . ($ family !== 'both ' ? "_ {$ family }" : '' )
42+ . (!empty ($ exTypeFilter ) ? '_exType_ ' . implode ('- ' , $ exTypeFilter ) : '' )
4043 . '.csv ' ;
4144
4245 $ path = $ this ->option ('path ' ) ?: $ defaultPath ;
@@ -48,15 +51,16 @@ public function handle()
4851 }
4952
5053 if ($ family === 'excellence ' || $ family === 'both ' ) {
51- $ rows = $ rows ->merge ($ this ->exportExcellence ($ start , $ end , $ inclusive , $ datePref ));
54+ $ rows = $ rows ->merge ($ this ->exportExcellence ($ start , $ end , $ inclusive , $ datePref, $ exTypeFilter ));
5255 }
5356
54- // Write merged CSV
57+ // Write merged CSV (now includes excellence_type columns)
5558 $ stream = fopen ('php://temp ' , 'w+ ' );
5659 fputcsv ($ stream , [
5760 'family ' , 'record_id ' , 'issued_at ' , 'event_date ' ,
5861 'status ' , 'owner_email ' , 'event_id ' , 'title ' ,
59- 'certificate_url ' , 'missing_url '
62+ 'certificate_url ' , 'missing_url ' ,
63+ 'excellence_type ' , 'excellence_type_norm '
6064 ]);
6165
6266 foreach ($ rows as $ r ) {
@@ -71,6 +75,8 @@ public function handle()
7175 $ r ['title ' ] ?? null ,
7276 $ r ['certificate_url ' ] ?? null ,
7377 !empty ($ r ['certificate_url ' ]) ? 0 : 1 ,
78+ $ r ['excellence_type ' ] ?? null ,
79+ $ r ['excellence_type_norm ' ] ?? null ,
7480 ]);
7581 }
7682
@@ -82,9 +88,9 @@ public function handle()
8288 $ this ->info ("Wrote {$ rows ->count ()} rows to storage/app/ {$ path }" );
8389 $ this ->line ('Breakdown: ' );
8490
85- // Print per -family monthly breakdowns for the memo
91+ // Per -family monthly breakdowns
8692 $ this ->printMonthly ('participations ' , $ start , $ end , $ inclusive , $ datePref );
87- $ this ->printMonthly ('excellence ' , $ start , $ end , $ inclusive , $ datePref );
93+ $ this ->printMonthly ('excellence ' , $ start , $ end , $ inclusive , $ datePref );
8894
8995 return self ::SUCCESS ;
9096 }
@@ -110,16 +116,28 @@ protected function pickDateColumn(string $table, string $preferred): ?string
110116 return null ;
111117 }
112118
113- /**
114- * Resolve the excellence table name on this server (case/variant safe).
115- */
116119 protected function excellenceTable (): ?string
117120 {
118121 if (Schema::hasTable ('excellences ' )) return 'excellences ' ;
119122 if (Schema::hasTable ('CertificatesOfExcellence ' )) return 'CertificatesOfExcellence ' ;
120123 return null ;
121124 }
122125
126+ protected function normalizeType (?string $ t ): ?string
127+ {
128+ if ($ t === null ) return null ;
129+ return strtolower (str_replace ('- ' , '' , $ t ));
130+ }
131+
132+ protected function parseExcellenceTypeFilter ($ opt ): array
133+ {
134+ if (!$ opt ) return [];
135+ $ parts = array_filter (array_map ('trim ' , explode (', ' , $ opt )));
136+ return array_values (array_unique (array_map (function ($ s ) {
137+ return $ this ->normalizeType ($ s );
138+ }, $ parts )));
139+ }
140+
123141 protected function exportParticipations (string $ start , string $ end , bool $ inclusive , string $ datePref )
124142 {
125143 $ table = 'participations ' ;
@@ -161,7 +179,7 @@ protected function exportParticipations(string $start, string $end, bool $inclus
161179 $ select [] = 'e.title as title ' ;
162180 } else {
163181 $ select [] = DB ::raw ('NULL as event_id ' );
164- $ select [] = DB ::raw ('NULL as title ' ); // `participations` has no native title
182+ $ select [] = DB ::raw ('NULL as title ' );
165183 }
166184
167185 return collect ($ q ->get ($ select ))->map (function ($ r ) {
@@ -175,11 +193,13 @@ protected function exportParticipations(string $start, string $end, bool $inclus
175193 'event_id ' => property_exists ($ r , 'event_id ' ) ? $ r ->event_id : null ,
176194 'title ' => $ r ->title ?? null ,
177195 'certificate_url ' => $ r ->certificate_url ?? null ,
196+ 'excellence_type ' => null ,
197+ 'excellence_type_norm ' => null ,
178198 ];
179199 });
180200 }
181201
182- protected function exportExcellence (string $ start , string $ end , bool $ inclusive , string $ datePref )
202+ protected function exportExcellence (string $ start , string $ end , bool $ inclusive , string $ datePref, array $ exTypeFilter = [] )
183203 {
184204 $ exTable = $ this ->excellenceTable ();
185205 if (!$ exTable ) return collect ();
@@ -201,6 +221,12 @@ protected function exportExcellence(string $start, string $end, bool $inclusive,
201221 if ($ urlCol ) $ q ->whereNotNull ("$ alias. $ urlCol " );
202222 }
203223
224+ // Optional Excellence type filtering (normalized)
225+ if (!empty ($ exTypeFilter ) && Schema::hasColumn ($ exTable , 'type ' )) {
226+ // use SQL normalization to match our normalizeType()
227+ $ q ->whereIn (DB ::raw ("LOWER(REPLACE( $ alias.type,'-','')) " ), $ exTypeFilter );
228+ }
229+
204230 // Build select list defensively
205231 $ select = ["$ alias.id as record_id " , DB ::raw ("$ dateExpr as issued_at " )];
206232 $ select [] = Schema::hasColumn ($ exTable ,'event_date ' ) ? "$ alias.event_date " : DB ::raw ('NULL as event_date ' );
@@ -217,6 +243,15 @@ protected function exportExcellence(string $start, string $end, bool $inclusive,
217243 elseif (Schema::hasColumn ($ exTable ,'url ' )) $ select [] = "$ alias.url as certificate_url " ;
218244 else $ select [] = DB ::raw ('NULL as certificate_url ' );
219245
246+ // Excellence types (raw + normalized)
247+ if (Schema::hasColumn ($ exTable ,'type ' )) {
248+ $ select [] = "$ alias.type as excellence_type " ;
249+ $ select [] = DB ::raw ("LOWER(REPLACE( $ alias.type,'-','')) as excellence_type_norm " );
250+ } else {
251+ $ select [] = DB ::raw ("NULL as excellence_type " );
252+ $ select [] = DB ::raw ("NULL as excellence_type_norm " );
253+ }
254+
220255 return collect ($ q ->get ($ select ))->map (function ($ r ) {
221256 return [
222257 'family ' => 'excellence ' ,
@@ -228,6 +263,8 @@ protected function exportExcellence(string $start, string $end, bool $inclusive,
228263 'event_id ' => $ r ->event_id ?? null ,
229264 'title ' => $ r ->title ?? null ,
230265 'certificate_url ' => $ r ->certificate_url ?? null ,
266+ 'excellence_type ' => $ r ->excellence_type ?? null ,
267+ 'excellence_type_norm ' => $ r ->excellence_type_norm ?? null ,
231268 ];
232269 });
233270 }
0 commit comments