diff --git a/app/Console/Commands/CertificatesIssues.php b/app/Console/Commands/CertificatesIssues.php index da5f2c03b..00d998f81 100644 --- a/app/Console/Commands/CertificatesIssues.php +++ b/app/Console/Commands/CertificatesIssues.php @@ -31,7 +31,7 @@ public function handle(): int { $issues = Participation::whereNull('participation_url') - ->where('status', '=', 'PENDING') + ->where('status', 'PENDING') ->where('created_at', '<', Carbon::now()->subMinutes(5))->get(); Log::info('certificate with issues: '.count($issues)); diff --git a/app/Console/Commands/CertificatesOfParticipationGeneration.php b/app/Console/Commands/CertificatesOfParticipationGeneration.php index f6ea73224..e61d1f4c5 100644 --- a/app/Console/Commands/CertificatesOfParticipationGeneration.php +++ b/app/Console/Commands/CertificatesOfParticipationGeneration.php @@ -28,7 +28,7 @@ class CertificatesOfParticipationGeneration extends Command public function handle(): int { - $participations = Participation::whereNull('participation_url')->where('status', '=', 'PENDING')->orderByDesc('created_at')->get(); + $participations = Participation::whereNull('participation_url')->where('status', 'PENDING')->orderByDesc('created_at')->get(); $this->info(count($participations).' certificates of participation to generate'); diff --git a/app/Console/Commands/EventsIndexOptimize.php b/app/Console/Commands/EventsIndexOptimize.php new file mode 100644 index 000000000..64822ce11 --- /dev/null +++ b/app/Console/Commands/EventsIndexOptimize.php @@ -0,0 +1,73 @@ +option('rollback'); + + $this->info(($rollback ? 'Dropping' : 'Adding') . ' indexes on events table...'); + + $indexes = [ + 'idx_status' => 'CREATE INDEX idx_status ON events(status)', + 'idx_country_iso' => 'CREATE INDEX idx_country_iso ON events(country_iso)', + 'idx_status_country' => 'CREATE INDEX idx_status_country ON events(status, country_iso)', + 'idx_start_date' => 'CREATE INDEX idx_start_date ON events(start_date)', + 'idx_end_date' => 'CREATE INDEX idx_end_date ON events(end_date)', + 'idx_activity_type' => 'CREATE INDEX idx_activity_type ON events(activity_type)', + 'idx_highlighted_status' => 'CREATE INDEX idx_highlighted_status ON events(highlighted_status)', + 'idx_lat_lon' => 'CREATE INDEX idx_lat_lon ON events(latitude, longitude)', + 'idx_creator_id' => 'CREATE INDEX idx_creator_id ON events(creator_id)', + 'idx_user_email' => 'CREATE INDEX idx_user_email ON events(user_email)', + 'idx_status_start' => 'CREATE INDEX idx_status_start ON events(status, start_date)', + ]; + + foreach ($indexes as $name => $sql) { + try { + if ($rollback) { + DB::statement("DROP INDEX {$name} ON events"); + $this->info("Dropped index: {$name}"); + } else { + $exists = DB::select( + "SELECT COUNT(1) as count FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'events' AND index_name = ?", + [$name] + ); + + if (!empty($exists) && $exists[0]->count == 0) { + DB::statement($sql); + $this->info("Created index: {$name}"); + } else { + $this->line("Index already exists: {$name}"); + } + } + } catch (\Throwable $e) { + $this->error("Failed to " . ($rollback ? 'drop' : 'create') . " index {$name}: " . $e->getMessage()); + } + } + + $this->info('Index operation completed.'); + return 0; + } +} diff --git a/app/Console/Commands/Excellence.php b/app/Console/Commands/Excellence.php index 647611feb..d564fc037 100644 --- a/app/Console/Commands/Excellence.php +++ b/app/Console/Commands/Excellence.php @@ -47,7 +47,7 @@ public function handle(): void //Select the winners from the Database $winners = []; foreach ($codeweek4all_codes as $codeweek4all_code) { - $creators = Event::whereYear('end_date', '=', $edition)->where('status', '=', 'APPROVED')->where('codeweek_for_all_participation_code', '=', $codeweek4all_code)->pluck('creator_id'); + $creators = Event::whereYear('end_date', '=', $edition)->where('status', 'APPROVED')->where('codeweek_for_all_participation_code', '=', $codeweek4all_code)->pluck('creator_id'); foreach ($creators as $creator) { if (! in_array($creator, $winners)) { $winners[] = $creator; diff --git a/app/Console/Commands/LocationExtraction.php b/app/Console/Commands/LocationExtraction.php index 2b59801aa..b6ebc31f1 100644 --- a/app/Console/Commands/LocationExtraction.php +++ b/app/Console/Commands/LocationExtraction.php @@ -32,7 +32,7 @@ public function handle(): void Event::whereNull('deleted_at')-> // where('id','=',163373)-> // where('creator_id',153701)-> - where('status', '=', 'APPROVED')-> + where('status', 'APPROVED')-> whereNull('location_id')->chunkById($this->step, function ($events, $index) { $this->reportProgress($index); diff --git a/app/Console/Commands/SyncEventAgesFromAudience.php b/app/Console/Commands/SyncEventAgesFromAudience.php new file mode 100644 index 000000000..362c22a03 --- /dev/null +++ b/app/Console/Commands/SyncEventAgesFromAudience.php @@ -0,0 +1,83 @@ + [1], // Pre-school children + '6-9' => [2], // Elementary school students + '10-12' => [2], // Upper primary → still Elementary + '13-15' => [3], // Lower secondary → High school + '16-18' => [3], // Upper secondary → High school + '19-25' => [4, 5], // Graduate & Post graduate students + 'over-25' => [6, 7, 9], // Employed + Unemployed adults + Teachers + ]; + + /** + * Execute the console command. + */ + public function handle() + { + $this->info('Syncing event.ages from audience_event...'); + + $map = self::AGE_AUDIENCE_MAP; + + $audienceToAges = []; + foreach ($map as $ageKey => $audienceIds) { + foreach ($audienceIds as $audienceId) { + $audienceToAges[$audienceId][] = $ageKey; + } + } + + $links = DB::table('audience_event') + ->select('event_id', 'audience_id') + ->get() + ->groupBy('event_id'); + + $updated = 0; + + foreach ($links as $eventId => $group) { + $ageKeys = collect($group) + ->flatMap(function ($row) use ($audienceToAges) { + return $audienceToAges[$row->audience_id] ?? []; + }) + ->unique() + ->values() + ->all(); + + if (!empty($ageKeys)) { + Event::where('id', $eventId)->update([ + 'ages' => json_encode($ageKeys), + ]); + $updated++; + } + } + + $this->info("Updated `ages` field for $updated events."); + return 0; + } +} diff --git a/app/Console/Commands/SyncThemesFromFinalList.php b/app/Console/Commands/SyncThemesFromFinalList.php new file mode 100644 index 000000000..0e83d0c9f --- /dev/null +++ b/app/Console/Commands/SyncThemesFromFinalList.php @@ -0,0 +1,267 @@ + 'AI & Generative AI', + 'Artificial intelligence' => 'AI & Generative AI', + + // Robotics, Drones, Devices + 'Robotics' => 'Robotics, Drones & Smart Devices', + 'Drones' => 'Robotics, Drones & Smart Devices', + 'Hardware' => 'Robotics, Drones & Smart Devices', + 'Digital Technologies' => 'Robotics, Drones & Smart Devices', + + // Web/App/Software Dev + 'Mobile app development' => 'Web, App & Software Development', + 'Web development' => 'Web, App & Software Development', + 'Software development' => 'Web, App & Software Development', + + // Game Design + 'Game design' => 'Game Design', + + // Cybersecurity & Data + 'Data manipulation and visualisation' => 'Cybersecurity & Data', // best match despite not direct + + // Visual/Block Programming + 'Basic programming concepts' => 'Visual/Block Programming', + 'Visual/Block programming' => 'Visual/Block Programming', + + // Art & Creative Coding + 'Art and creativity' => 'Art & Creative Coding', + + // Internet of Things & Wearables + 'Sensors' => 'Internet of Things & Wearables', + 'Internet of things and wearable computing' => 'Internet of Things & Wearables', + + // AR/VR/3D + '3D printing' => 'AR, VR & 3D Technologies', + 'Augmented reality' => 'AR, VR & 3D Technologies', + + // Digital Careers + 'Digital careers' => 'Digital Careers & Learning Pathways', + 'Digital learning pathways' => 'Digital Careers & Learning Pathways', + + // Soft Skills + 'Soft Skills' => 'Digital Literacy & Soft Skills', + + // Unplugged & Playful + 'Unplugged activities' => 'Unplugged & Playful Activities', + 'Playful coding activities' => 'Unplugged & Playful Activities', + + // Diversity + 'Promoting diversity' => 'Promoting Diversity & Inclusion', + + // Awareness + 'Motivation and awareness raising' => 'Awareness & Inspiration', + + // Other + 'Other' => 'Other', + ]; + + protected $finalThemes = [ + [17, 'AI & Generative AI', 15], + [6, 'Robotics, Drones & Smart Devices', 1], + [2, 'Web, App & Software Development', 4], + [13, 'Game Design', 10], + [5, 'Cybersecurity & Data', 2], + [1, 'Visual/Block Programming', 5], + [11, 'Art & Creative Coding', 8], + [14, 'Internet of Things & Wearables', 11], + [16, 'AR, VR & 3D Technologies', 12], + [3, 'Digital Careers & Learning Pathways', 13], + [4, 'Digital Literacy & Soft Skills', 14], + [9, 'Unplugged & Playful Activities', 6], + [19, 'Promoting Diversity & Inclusion', 17], + [18, 'Awareness & Inspiration', 16], + [8, 'Other', 18], + ]; + + /** + * Execute the console command. + */ + public function handle() + { + $restoreOnly = $this->option('restore'); + + if ($restoreOnly) { + return $this->restoreBackup(); + } + + $this->info('Starting themes sync and migration...'); + + DB::beginTransaction(); + + try { + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + config(['database.connections.mysql.strict' => false]); + DB::reconnect(); + + $eventThemeOld = DB::table('event_theme') + ->join('themes', 'event_theme.theme_id', '=', 'themes.id') + ->select('event_theme.event_id', 'themes.name as old_theme_name', 'event_theme.theme_id') + ->get(); + + if ( !Storage::disk('excel')->exists(self::BACKUP_FILE)) { + Storage::disk('excel')->put(self::BACKUP_FILE, $eventThemeOld->toJson(JSON_PRETTY_PRINT)); + $this->info('Backed up current event_theme to ' . self::BACKUP_FILE); + } + else { + $this->info('No update, Backed up current event_theme exist in ' . self::BACKUP_FILE); + } + + DB::table('event_theme')->delete(); + DB::table('themes')->delete(); + + $finalThemesMap = collect($this->finalThemes)->map(function ($theme) { + return [ + 'id' => $theme[0], + 'name' => $theme[1], + 'order' => $theme[2], + ]; + }); + + DB::table('themes')->insert($finalThemesMap->toArray()); + + // Set AUTO_INCREMENT to max(id) + 1 + $maxId = collect($this->finalThemes)->max(fn($theme) => $theme[0]); + DB::statement('ALTER TABLE themes AUTO_INCREMENT = ' . ($maxId + 1)); + + $newThemes = DB::table('themes')->get()->keyBy('name'); + + $mappedRows = []; + + foreach ($eventThemeOld as $item) { + $newName = self::OLD_TO_NEW_THEME_MAP[$item->old_theme_name] ?? null; + if ($newName && isset($newThemes[$newName])) { + $mappedRows[] = [ + 'event_id' => $item->event_id, + 'theme_id' => $newThemes[$newName]->id, + ]; + } + } + + $validatedRows = []; + + foreach (array_chunk($mappedRows, 500) as $chunk) { + $eventIds = array_column($chunk, 'event_id'); + + $existingIds = DB::table('events') + ->whereIn('id', $eventIds) + ->pluck('id') + ->toArray(); + + $validatedRows = array_merge($validatedRows, array_filter($chunk, fn($row) => in_array($row['event_id'], $existingIds))); + } + + $uniqueRows = collect($validatedRows) + ->unique(fn($row) => $row['event_id'] . '-' . $row['theme_id']) + ->values() + ->all(); + + foreach (array_chunk($uniqueRows, 1000) as $chunk) { + DB::table('event_theme')->insert($chunk); + } + + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + config(['database.connections.mysql.strict' => true]); + DB::reconnect(); + + DB::commit(); + + $this->info("Themes synced and event_theme migrated successfully. Total migrated: " . count($uniqueRows)); + } catch (\Throwable $e) { + DB::rollBack(); + $this->error("Error syncing themes: " . $e->getMessage()); + } + + return 0; + } + + protected function restoreBackup(): int + { + $this->info('Restoring event_theme from backup...'); + + if (!Storage::disk('excel')->exists(self::BACKUP_FILE)) { + $this->error('Backup file not found: ' . self::BACKUP_FILE); + return 1; + } + + $raw = Storage::disk('excel')->get(self::BACKUP_FILE); + $data = json_decode($raw, true); + $data = collect($data)->map(function ($row) { + return [ + 'event_id' => $row['event_id'], + 'theme_id' => $row['theme_id'], + ]; + })->values()->all(); + + if (empty($data)) { + $this->error('Backup is empty or invalid.'); + return 1; + } + + try { + config(['database.connections.mysql.strict' => false]); + DB::reconnect(); + + DB::beginTransaction(); + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + + DB::table('event_theme')->delete(); + DB::table('themes')->delete(); + + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + DB::commit(); + + $this->info('Running legacy ThemeTableSeeder...'); + $this->callSilent('db:seed', [ + '--class' => 'ThemeTableSeeder', + ]); + + DB::beginTransaction(); + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + + foreach (array_chunk($data, 1000) as $chunk) { + DB::table('event_theme')->insert($chunk); + } + + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + DB::commit(); + + config(['database.connections.mysql.strict' => true]); + DB::reconnect(); + + $this->info('Restored event_theme successfully: ' . count($data) . ' rows.'); + } catch (\Throwable $e) { + DB::rollBack(); + $this->error('Restore failed: ' . $e->getMessage()); + return 1; + } + + return 0; + } +} diff --git a/app/Event.php b/app/Event.php index 75fd24aad..04efe209a 100644 --- a/app/Event.php +++ b/app/Event.php @@ -16,6 +16,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Spatie\Activitylog\LogOptions; use Stevebauman\Purify\Casts\PurifyHtmlOnGet; @@ -67,6 +68,58 @@ class Event extends Model 'location_id', 'leading_teacher_tag', 'mass_added_for', + 'activity_format', + 'duration', + 'recurring_event', + 'recurring_type', + 'males_count', + 'females_count', + 'other_count', + 'is_extracurricular_event', + 'is_standard_school_curriculum', + 'is_use_resource', + 'ages' + ]; + + public const ACTIVITY_FORMATS = [ + 'coding-camp', + 'summer-camp', + 'weekend-course', + 'evening-course', + 'careerday', + 'university-visit', + 'coding-home', + 'code-week-challenge', + 'competition', + 'other', + ]; + + public const DURATIONS = [ + '0-1', + '1-2', + '2-4', + 'over-4', + ]; + + public const RECURRING_TYPES = [ + 'consecutive', + 'individual', + ]; + + public const RECURRING_EVENTS = [ + 'daily', + 'weekly', + 'monthly', + ]; + + public const AGES = [ + 'under-5', + '6-9', + '10-12', + '13-15', + '16-18', + '19-25', + 'over-25', ]; // protected $policies = [ @@ -74,7 +127,7 @@ class Event extends Model // Event::class => EventPolicy::class // ]; - //protected $appends = ['LatestModeration']; + protected $appends = ['picture_path']; public function getUrlAttribute() { if (!empty($this->slug)) { @@ -109,6 +162,12 @@ protected function casts(): array 'title' => PurifyHtmlOnGet::class, 'location' => PurifyHtmlOnGet::class, 'language' => PurifyHtmlOnGet::class, + + 'activity_format' => 'array', + 'is_extracurricular_event' => 'boolean', + 'is_standard_school_curriculum' => 'boolean', + 'ages' => 'array', + 'is_use_resource' => 'boolean', ]; } @@ -140,6 +199,8 @@ public function picture_path() return $this->picture; } + // For local test + // return Storage::disk('public')->url($this->picture); return config('codeweek.aws_url').$this->picture; } else { return 'https://s3-eu-west-1.amazonaws.com/codeweek-dev/events/pictures/event_default_picture.png'; @@ -212,7 +273,7 @@ public function scopeFilter($query, EventFilters $filters) public static function getByYear($year) { - $events = Event::where('status', 'like', 'APPROVED')->where( + $events = Event::where('status', 'APPROVED')->where( 'start_date', '>', Carbon::createFromDate($year, 1, 1) diff --git a/app/Filters/EventFilters.php b/app/Filters/EventFilters.php index 5686edfbb..c01cbd3cb 100755 --- a/app/Filters/EventFilters.php +++ b/app/Filters/EventFilters.php @@ -3,7 +3,6 @@ namespace App\Filters; use App\Tag; -use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\Support\Str; @@ -15,7 +14,20 @@ class EventFilters extends Filters * * @var array */ - protected $filters = ['countries', 'query', 'themes', 'audiences', 'types', 'year', 'creator_id', 'tag']; + protected $filters = [ + 'countries', + 'query', + 'themes', + 'audiences', + 'types', + 'year', + 'creator_id', + 'tag', + 'formats', + 'ages', + 'languages', + 'start_date' + ]; public function __construct(Request $request) { @@ -50,13 +62,11 @@ protected function countries($countries): Builder protected function year($year) { return $this->builder->whereYear('end_date', '=', $year); - } protected function creator_id($creator_id) { return $this->builder->where('creator_id', '=', $creator_id); - } protected function query($query) @@ -85,7 +95,6 @@ protected function themes($themes) return $this->builder ->leftJoin('event_theme', 'events.id', '=', 'event_theme.event_id') ->whereIn('event_theme.theme_id', $themesIds); - } protected function tag($tag) @@ -104,7 +113,6 @@ protected function tag($tag) return $this->builder ->leftJoin('event_tag', 'events.id', '=', 'event_tag.event_id') ->where('event_tag.tag_id', $selectedTag->id); - } protected function audiences($audiences) @@ -119,7 +127,6 @@ protected function audiences($audiences) return $this->builder ->leftJoin('audience_event', 'events.id', '=', 'audience_event.event_id') ->whereIn('audience_event.audience_id', $audiencesIds); - } protected function types($types) @@ -129,11 +136,62 @@ protected function types($types) return; } - $keys = collect($types)->pluck('key')->all(); + $keys = collect($types)->pluck('id')->all(); $result = $this->builder->whereIn('activity_type', $keys); return $result; + } + + protected function start_date($start_date) + { + if (!empty($start_date)) { + return $this->builder->where('start_date', '>=', $start_date); + } + return; + } + + protected function languages($languages) + { + + if (empty($languages)) { + return; + } + + $keys = collect($languages)->pluck('id')->all(); + + $result = $this->builder->whereIn('language', $keys); + + return $result; + } + protected function formats($formats) + { + if (empty($formats)) { + return; + } + + $keys = collect($formats)->pluck('id')->all(); + + $this->builder->where(function ($query) use ($keys) { + foreach ($keys as $key) { + $query->orWhereRaw("JSON_CONTAINS(activity_format, '\"$key\"')"); + } + }); + } + + protected function ages($ages) + { + if (empty($ages)) { + return; + } + + $keys = collect($ages)->pluck('id')->all(); + + $this->builder->where(function ($query) use ($keys) { + foreach ($keys as $key) { + $query->orWhereRaw("JSON_CONTAINS(ages, '\"$key\"')"); + } + }); } } diff --git a/app/Helpers/Codeweek4AllHelper.php b/app/Helpers/Codeweek4AllHelper.php index fe8745454..6500c58fa 100644 --- a/app/Helpers/Codeweek4AllHelper.php +++ b/app/Helpers/Codeweek4AllHelper.php @@ -17,7 +17,7 @@ public static function kpis($code, $edition = null) $result = Event::select(DB::raw('count(DISTINCT creator_id) as total_creators, sum(participants_count) as participants_count, count(id) as event_count, codeweek_for_all_participation_code')) ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ['codeweek_for_all_participation_code', '=', $code], ]) ->whereYear('end_date', '=', $edition) @@ -39,7 +39,7 @@ public static function countries($code, $edition = null) select(DB::raw('count(DISTINCT creator_id) as total_creators, sum(participants_count) as total_participants, codeweek_for_all_participation_code')) ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ]) ->whereYear('end_date', '=', $edition) ->groupBy('codeweek_for_all_participation_code') @@ -51,7 +51,7 @@ public static function countries($code, $edition = null) $result = Event::select(DB::raw('sum(participants_count) as participants_count, count(id) as event_count, codeweek_for_all_participation_code')) ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ['codeweek_for_all_participation_code', '=', $code], ]) ->whereYear('end_date', '=', $edition) @@ -71,7 +71,7 @@ public static function getDetailsByCodeweek4All(array $toArray, $edition = null) return Event::select(DB::raw('codeweek_for_all_participation_code, sum(participants_count) as total_participants, count(DISTINCT creator_id) as total_creators, count(DISTINCT country_iso) as total_countries, count(id) as total_activities, ((100.0*count(reported_at))/count(*)) as reporting_percentage')) ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ]) ->whereYear('end_date', '=', $edition) ->whereIn('codeweek_for_all_participation_code', $toArray) @@ -88,7 +88,7 @@ public static function getCountriesByCodeweek4All($code, $edition = null) $result = Event::select(DB::raw('countries.name , count(events.id) as event_per_country')) ->join('countries', 'events.country_iso', '=', 'countries.iso') ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ['codeweek_for_all_participation_code', 'like', $code], ]) ->whereYear('end_date', '=', $edition) @@ -103,7 +103,7 @@ public static function getInitiatorByCodeweek4All($code) $result = Event::select(DB::raw('users.email')) ->join('users', 'events.creator_id', '=', 'users.id') ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ['codeweek_for_all_participation_code', 'like', $code], ]) ->orderBy('events.created_at', 'asc') diff --git a/app/Helpers/EventHelper.php b/app/Helpers/EventHelper.php index 378b3cc71..59639afa5 100644 --- a/app/Helpers/EventHelper.php +++ b/app/Helpers/EventHelper.php @@ -20,7 +20,7 @@ public static function getCloseEvents($longitude, $latitude, $id = 0, $num = 3) } $events = Event::selectRaw( - 'id, title, slug, start_date, end_date, picture, description, picture, creator_id, + 'id, title, slug, start_date, end_date, picture, description, picture, creator_id, highlighted_status, recurring_event, ( 6371 * acos( cos( radians(?) ) * cos( radians( latitude ) ) * @@ -31,7 +31,7 @@ public static function getCloseEvents($longitude, $latitude, $id = 0, $num = 3) AS distance', [$latitude, $longitude, $latitude] ) - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->where('id', '<>', $id) ->where('end_date', '>', Carbon::now()) ->orderBy('distance') @@ -55,7 +55,7 @@ public static function getPendindEvents() array_push($countries, $country->country_iso); } - $events = Event::where('status', '=', 'PENDING') + $events = Event::where('status', 'PENDING') ->distinct() ->select('country_iso') ->where('start_date', '>', Carbon::createFromDate(2018, 1, 1)) @@ -67,7 +67,7 @@ public static function getPendindEvents() public static function getReportedEventsWithoutCertificates() { - $events = Event::where('status', '=', 'APPROVED') + $events = Event::where('status', 'APPROVED') ->whereNotNull('reported_at') ->whereNull('certificate_url') ->whereNotNull('approved_by') @@ -81,7 +81,7 @@ public static function getReportedEventsWithoutCertificates() private static function getPendingEventsForCountry($country) { - $events = Event::where('status', '=', 'PENDING') + $events = Event::where('status', 'PENDING') ->where('start_date', '>', Carbon::createFromDate(2018, 1, 1)) ->where('country_iso', $country) ->get(); @@ -92,7 +92,7 @@ private static function getPendingEventsForCountry($country) private static function getPendingEventsCountForCountry($country) { - $count = Event::where('status', '=', 'PENDING') + $count = Event::where('status', 'PENDING') ->select('country_iso') ->where('start_date', '>', Carbon::createFromDate(2018, 1, 1)) ->where('country_iso', $country) @@ -103,7 +103,7 @@ private static function getPendingEventsCountForCountry($country) private static function getEventsQuery() { - return Event::where('status', '=', 'PENDING') + return Event::where('status', 'PENDING') ->where('start_date', '>', Carbon::createFromDate(2018, 1, 1)); } @@ -140,7 +140,7 @@ public static function getNextPendingEvent(Event $event, ?string $country = null return self::getEventsQuery()->where('id', '>', $event->id)->limit(1)->first(); } else { //Get pending events count for specific country - return Event::where('status', '=', 'PENDING') + return Event::where('status', 'PENDING') ->where('country_iso', $country) ->where('start_date', '>', Carbon::createFromDate(2018, 1, 1)) ->where('id', '<>', $event->id)->limit(1)->first(); diff --git a/app/Helpers/ExcellenceWinnersHelper.php b/app/Helpers/ExcellenceWinnersHelper.php index 0c902cd48..e6a9b550b 100644 --- a/app/Helpers/ExcellenceWinnersHelper.php +++ b/app/Helpers/ExcellenceWinnersHelper.php @@ -33,7 +33,7 @@ public static function criteria1($edition) $codes = Event::select(DB::raw('sum(participants_count) as total_participants, codeweek_for_all_participation_code')) ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ]) ->whereYear('end_date', '=', $edition) ->groupBy('codeweek_for_all_participation_code') @@ -52,7 +52,7 @@ public static function criteria2($edition) $codes = Event::select(DB::raw('count(DISTINCT creator_id) as total_creators, sum(participants_count) as total_participants, codeweek_for_all_participation_code')) ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ]) ->whereYear('end_date', '=', $edition) ->groupBy('codeweek_for_all_participation_code') @@ -72,7 +72,7 @@ public static function criteria3($edition) $codes = Event::select(DB::raw('count(DISTINCT country_iso) as total_countries, sum(participants_count) as total_participants,codeweek_for_all_participation_code')) ->where([ - ['status', 'like', 'APPROVED'], + ['status', 'APPROVED'], ]) ->whereYear('end_date', '=', $edition) ->groupBy('codeweek_for_all_participation_code') diff --git a/app/Helpers/MailingHelper.php b/app/Helpers/MailingHelper.php index 443b0e3e1..9aad7f671 100644 --- a/app/Helpers/MailingHelper.php +++ b/app/Helpers/MailingHelper.php @@ -11,7 +11,7 @@ public static function getActiveCreators($country) $activeIds = DB::table('events') ->join('users', 'users.id', '=', 'events.creator_id') - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->where('users.receive_emails', true) ->where('events.country_iso', '=', $country) ->whereNull('users.deleted_at') @@ -25,7 +25,7 @@ public static function getActiveCreators($country) return DB::table('events') ->join('users', 'users.id', '=', 'events.creator_id') - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->whereNull('events.deleted_at') ->whereIntegerInRaw('events.creator_id', $activeIds) ->groupBy('users.email') diff --git a/app/Helpers/ReminderHelper.php b/app/Helpers/ReminderHelper.php index 442fb665c..ee43f99cd 100644 --- a/app/Helpers/ReminderHelper.php +++ b/app/Helpers/ReminderHelper.php @@ -15,7 +15,7 @@ public static function getInactiveCreators($edition) $activeIds = DB::table('events') ->join('users', 'users.id', '=', 'events.creator_id') //->where('creator_id','=',$this->id) - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->where('users.receive_emails', true) ->whereNull('users.deleted_at') ->whereNull('events.deleted_at') @@ -29,7 +29,7 @@ public static function getInactiveCreators($edition) return DB::table('events') ->join('users', 'users.id', '=', 'events.creator_id') //->where('creator_id','=',$this->id) - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->whereNull('events.deleted_at') ->where(function ($query) use ($edition) { return $query->whereYear('events.end_date', '=', $edition - 1); @@ -49,7 +49,7 @@ public static function getActiveCreators() $activeIds = DB::table('events') ->join('users', 'users.id', '=', 'events.creator_id') - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->where('users.receive_emails', true) ->whereNull('users.deleted_at') ->whereNull('events.deleted_at') @@ -62,7 +62,7 @@ public static function getActiveCreators() return DB::table('events') ->join('users', 'users.id', '=', 'events.creator_id') - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->whereNull('events.deleted_at') ->whereIntegerInRaw('events.creator_id', $activeIds) ->groupBy('users.email') diff --git a/app/Http/Controllers/Api/EventsController.php b/app/Http/Controllers/Api/EventsController.php index 994df55ec..d80d1516d 100644 --- a/app/Http/Controllers/Api/EventsController.php +++ b/app/Http/Controllers/Api/EventsController.php @@ -105,7 +105,7 @@ public function germany(Request $request) ]); $collection = \App\Http\Resources\EventResource::collection( - Event::where('status', 'like', 'APPROVED') + Event::where('status', 'APPROVED') ->where('country_iso', 'DE') ->whereYear('end_date', '=', $validated['year']) ->get() @@ -158,7 +158,7 @@ public function geobox(Request $request) } $collection = \App\Http\Resources\EventResource::collection( - Event::where('status', 'like', 'APPROVED') + Event::where('status', 'APPROVED') ->where($box) ->whereYear('end_date', '=', $year) ->get() diff --git a/app/Http/Controllers/EventController.php b/app/Http/Controllers/EventController.php index 6ae8701ca..76af99321 100755 --- a/app/Http/Controllers/EventController.php +++ b/app/Http/Controllers/EventController.php @@ -26,7 +26,7 @@ class EventController extends Controller */ public function __construct() { - $this->middleware('auth')->except(['index', 'show', 'my']); + $this->middleware('auth')->except(['show', 'my']); } public function my(): View @@ -49,40 +49,6 @@ public function my(): View // // } - /** - * Display a listing of the resource. - */ - public function index(Request $request): View - { - $years = range(Carbon::now()->year, 2014, -1); - - $selectedYear = $request->input('year') - ? $request->input('year') - : Carbon::now()->year; - - $iso_country_of_user = User::getGeoIPData()->iso_code; - - $ambassadors = User::role('ambassador') - ->where('country_iso', '=', $iso_country_of_user) - ->get(); - - return view('events')->with([ - 'events' => $this->eventsNearMe(), - 'years' => $years, - 'selectedYear' => $selectedYear, - 'countries' => Country::withActualYearEvents(), - 'current_country_iso' => $iso_country_of_user, - 'ambassadors' => $ambassadors, - ]); - } - - private function eventsNearMe() - { - $geoip = User::getGeoIPData(); - - return EventHelper::getCloseEvents($geoip->lon, $geoip->lat); - } - /** * Show the form for creating a new resource. */ @@ -186,7 +152,7 @@ public function edit(Event $event): View ->pluck('id') ->toArray(); $selected_audiences = implode(',', $selected_audiences); - $selected_country = $event->country()->first()->iso; + $selected_country = optional($event->country()->first())->iso; $selected_language = is_null($event->language) ? 'en' : $event->language; diff --git a/app/Http/Controllers/ExcellenceWinnersController.php b/app/Http/Controllers/ExcellenceWinnersController.php index 1222f2770..dd68a311d 100644 --- a/app/Http/Controllers/ExcellenceWinnersController.php +++ b/app/Http/Controllers/ExcellenceWinnersController.php @@ -35,14 +35,14 @@ public function list(Request $request, $edition = 2024): View $details = ExcellenceWinnersHelper::query($edition, false); $total_events = DB::table('events') - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') //->where('codeweek_for_all_participation_code', '<>', 'cw19-apple-eu') ->whereYear('end_date', '=', $edition) ->whereNull('deleted_at') ->count(); $total_reported = DB::table('events') - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->whereNotNull('reported_at') ->whereNull('deleted_at') ->whereYear('end_date', '=', $edition) diff --git a/app/Http/Controllers/ScoreboardController.php b/app/Http/Controllers/ScoreboardController.php index ecc6e62a0..9d802a7d2 100644 --- a/app/Http/Controllers/ScoreboardController.php +++ b/app/Http/Controllers/ScoreboardController.php @@ -38,7 +38,7 @@ public function index(Request $request) $total = Cache::remember('total_' . $edition, $cache_time, function () use ($edition) { Log::info("Setting cache for scoreboard total in " . $edition); return DB::table('events') - ->where('status', "=", "APPROVED") + ->where('status', "APPROVED") ->whereNull('deleted_at') ->whereYear('end_date', '=', $edition) ->count(); @@ -52,7 +52,7 @@ public function index(Request $request) $events = DB::table('events') ->join('countries', 'events.country_iso', '=', 'countries.iso') ->select('countries.iso as country_iso', 'countries.name as country_name', 'countries.population as country_population', DB::raw('count(*) as total')) - ->where('status', "=", "APPROVED") + ->where('status', "APPROVED") ->whereYear('end_date', '=', $edition) ->whereNull('deleted_at') ->where('countries.parent', "=", "") @@ -63,7 +63,7 @@ public function index(Request $request) $eventsFromDependencies = DB::table('events') ->join('countries', 'events.country_iso', '=', 'countries.iso') ->select('countries.population as population', 'countries.parent as iso', DB::raw('count(*) as total')) - ->where('status', "=", "APPROVED") + ->where('status', "APPROVED") ->whereNull('deleted_at') ->whereYear('end_date', '=', $edition) ->whereNotNull('countries.parent') diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 9b59747a4..265e93fa7 100755 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -60,74 +60,46 @@ public function search(Request $request): View public function searchPOST(EventFilters $filters, Request $request) { - $events = $this->getEvents($filters); + $paginatedEvents = $this->getPaginatedEvents($filters); - //Log::info($request->input('page')); if ($request->input('page')) { - $result = [$events]; - } else { - Log::info('no page'); - $eventsMap = $this->getAllEventsToMap($filters); - $result = [$events, $eventsMap]; + return [$paginatedEvents]; } - return $result; + $mapData = $this->transformEventsForMap($filters); + + return [$paginatedEvents, $mapData]; } - protected function getEvents(EventFilters $filters) + protected function getPaginatedEvents(EventFilters $filters) { - - $events = Event::where('status', 'like', 'APPROVED') + return Event::where('status', 'APPROVED') ->filter($filters) + ->orderByRaw("start_date > ? desc", [Carbon::today()]) ->orderBy('start_date') - ->get() - ->groupBy(function ($event) { - if ($event->start_date <= Carbon::today()) { - return 'past'; - } - - return 'future'; - }); - - if (is_null($events->get('future')) || is_null($events->get('past'))) { - return $events->flatten()->paginate(12); - } - - return $events->get('future')->merge($events->get('past'))->paginate(12); + ->paginate(12); } - protected function getAllEventsToMap(EventFilters $filters) + protected function transformEventsForMap(EventFilters $filters) { - $flattened = Arr::flatten($filters->getFilters()); $filtered = array_filter($flattened, fn($v) => $v !== null && $v !== ''); - $composed_key = implode(',', $filtered); + $composed_key = 'map_' . implode(',', $filtered); - if (empty($composed_key)) { - Log::info('Skipping cache due to empty composed_key'); - return Event::where('status', 'APPROVED') - ->filter($filters) - ->get() - ->groupBy('country'); - } + return Cache::remember($composed_key, 300, function () use ($filters) { + $grouped = []; - $value = Cache::get($composed_key, function () use ($composed_key, $filters) { - Log::info("Building cache [{$composed_key}]"); - $events = Event::where('status', 'like', 'APPROVED') + Event::select('id', 'geoposition', 'country_iso') // Only required fields + ->where('status', 'APPROVED') ->filter($filters) - ->get(); - - $events = $this->eventTransformer->transformCollection($events); - - $events = $events->groupBy('country'); - - Cache::put($composed_key, $events, 5 * 60); - - return $events; + ->cursor() + ->each(function ($event) use (&$grouped) { + $transformed = app(\App\Http\Transformers\EventTransformer::class)->transform($event); + $country = $transformed['country'] ?? 'unknown'; + $grouped[$country][] = $transformed; + }); + + return collect($grouped); }); - - Log::info("Serving from cache [{$composed_key}]"); - - return $value; } } diff --git a/app/Http/Requests/EventRequest.php b/app/Http/Requests/EventRequest.php index 3e37fd724..eb506c159 100644 --- a/app/Http/Requests/EventRequest.php +++ b/app/Http/Requests/EventRequest.php @@ -31,6 +31,7 @@ public function rules(): array 'title' => 'required|min:5', 'description' => 'required|min:5', 'organizer' => 'required', + 'duration' => 'required', 'location' => 'required_unless:activity_type,open-online,invite-online', 'event_url' => 'required_if:activity_type,open-online,invite-online', 'language' => ['required', $this->in($languages)], @@ -38,6 +39,9 @@ public function rules(): array 'end_date' => 'required|after:start_date', 'audience' => ['required', new ValidAudience], 'theme' => ['required', new ValidTheme], + 'participants_count' => 'required', + 'ages' => 'required', + 'is_extracurricular_event' => 'required|boolean', 'country_iso' => 'required|exists:countries,iso', 'user_email' => 'required', 'organizer_type' => 'required', @@ -50,14 +54,20 @@ public function rules(): array public function messages(): array { return [ + 'activity_type.required' => 'Please select an activity type.', 'title.required' => 'Please enter a title for your event.', 'description.required' => 'Please write a short description of what the event is about.', 'organizer.required' => 'Please enter an organizer.', + 'duration.required' => 'Please specify the event duration.', 'location.required' => 'Please enter a location.', 'start_date.required' => 'Please enter a valid date and time (example: 2014-10-22 18:00).', 'end_date.required' => 'Please enter a valid date and time (example: 2014-10-22 18:00).', 'audience.required' => 'If unsure, choose Other and provide more information in the description.', 'theme.required' => 'If unsure, choose Other and provide more information in the description.', + 'participants_count.required' => 'Please specify the expected number of participants.', + 'ages.required' => 'Please select at least one age group.', + 'is_extracurricular_event.required' => 'Please specify if this is an extracurricular event.', + 'is_extracurricular_event.boolean' => 'The extracurricular event field must be true or false.', 'country.required' => 'The event\'s location should be in Europe.', 'event_url.url' => 'The activity\'s web page address should be a valid URL.', 'event_url.required' => 'The activity\'s web page is required for online activities.', diff --git a/app/Imports/AppleEventsImport.php b/app/Imports/AppleEventsImport.php index 229afaa3c..91ea0b221 100644 --- a/app/Imports/AppleEventsImport.php +++ b/app/Imports/AppleEventsImport.php @@ -7,16 +7,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; -use PhpOffice\PhpSpreadsheet\Shared\Date; -class AppleEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class AppleEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { - public function parseDate($date) - { - return Date::excelToDateTimeObject($date); - } - public function model(array $row): ?Model { @@ -45,6 +38,41 @@ public function model(array $row): ?Model 'latitude' => $row['latitude'], 'language' => strtolower(explode('_', $row['language'])[0]), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/AvandeEventsImport.php b/app/Imports/AvandeEventsImport.php index 7ab7b435b..7d1d678e2 100644 --- a/app/Imports/AvandeEventsImport.php +++ b/app/Imports/AvandeEventsImport.php @@ -9,10 +9,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class AvandeEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class AvandeEventsImport extends AppleEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -72,6 +71,41 @@ public function model(array $row): ?Model 'language' => !empty($row['language']) ? strtolower(explode('_', $row['language'])[0]) : 'en', 'approved_by' => 19588, 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/BaseEventsImport.php b/app/Imports/BaseEventsImport.php new file mode 100644 index 000000000..fbe106889 --- /dev/null +++ b/app/Imports/BaseEventsImport.php @@ -0,0 +1,40 @@ +map(fn($v) => trim($v)) + ->filter(fn($v) => in_array($v, $valid, true)) + ->values() + ->all(); + } + + protected function validateSingleChoice(?string $input, array $valid): ?string + { + return in_array($input, $valid, true) ? $input : null; + } +} diff --git a/app/Imports/BulgariaEventsImport.php b/app/Imports/BulgariaEventsImport.php index 3f50d5184..cf34ed8aa 100644 --- a/app/Imports/BulgariaEventsImport.php +++ b/app/Imports/BulgariaEventsImport.php @@ -8,10 +8,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class BulgariaEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class BulgariaEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -48,6 +47,41 @@ public function model(array $row): ?Model 'longitude' => $row['longitude'], 'latitude' => $row['latitude'], 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/CoderDojoEventsImport.php b/app/Imports/CoderDojoEventsImport.php index 367457081..cc213a98b 100644 --- a/app/Imports/CoderDojoEventsImport.php +++ b/app/Imports/CoderDojoEventsImport.php @@ -3,15 +3,13 @@ namespace App\Imports; use App\Event; -use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; -class CoderDojoEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class CoderDojoEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -65,6 +63,41 @@ public function model(array $row): ?Model 'language' => !empty($row['language']) ? trim($row['language']) : 'nl', 'approved_by' => 19588, 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/CodingActivitiesEUCodeWeekSiteImport.php b/app/Imports/CodingActivitiesEUCodeWeekSiteImport.php index 413e1226b..9eb268af0 100644 --- a/app/Imports/CodingActivitiesEUCodeWeekSiteImport.php +++ b/app/Imports/CodingActivitiesEUCodeWeekSiteImport.php @@ -3,16 +3,13 @@ namespace App\Imports; use App\Event; -use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Facades\Log; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class CodingActivitiesEUCodeWeekSiteImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class CodingActivitiesEUCodeWeekSiteImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -71,6 +68,41 @@ public function model(array $row): ?Model 'language' => $row['language'], 'approved_by' => 19588, 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/DutchDanceEventsImport.php b/app/Imports/DutchDanceEventsImport.php index f0231de4a..7524aab59 100644 --- a/app/Imports/DutchDanceEventsImport.php +++ b/app/Imports/DutchDanceEventsImport.php @@ -10,10 +10,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class DutchDanceEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class DutchDanceEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -64,6 +63,41 @@ public function model(array $row): ?Model 'latitude' => $row['longitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/DutchMoorlagEventsImport.php b/app/Imports/DutchMoorlagEventsImport.php index 344ea3465..5179f39b9 100644 --- a/app/Imports/DutchMoorlagEventsImport.php +++ b/app/Imports/DutchMoorlagEventsImport.php @@ -8,10 +8,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class DutchMoorlagEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class DutchMoorlagEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -48,6 +47,41 @@ public function model(array $row): ?Model 'latitude' => $row['longitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/DutchSimoneEventsImport.php b/app/Imports/DutchSimoneEventsImport.php index eed2bca19..fda1bd800 100644 --- a/app/Imports/DutchSimoneEventsImport.php +++ b/app/Imports/DutchSimoneEventsImport.php @@ -8,10 +8,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class DutchSimoneEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class DutchSimoneEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -48,6 +47,41 @@ public function model(array $row): ?Model 'latitude' => $row['latitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/EventiEventsImport.php b/app/Imports/EventiEventsImport.php index 6b7c7b566..c4bd15672 100644 --- a/app/Imports/EventiEventsImport.php +++ b/app/Imports/EventiEventsImport.php @@ -3,96 +3,129 @@ namespace App\Imports; use App\Event; -use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; -class EventiEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class EventiEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { - public function parseDate($date) - { - $arr = explode(',', $date); - array_shift($arr); - return implode($arr); - } - - public function model(array $row): ?Model - { - // Validate required fields - if ( - empty($row['activity_title']) || - empty($row['name_of_organisation']) || - empty($row['description']) || - empty($row['type_of_organisation']) || - empty($row['activity_type']) || - empty($row['country']) || - empty($row['start_date']) || - empty($row['end_date']) - ) { - Log::error('Missing required fields in row'); - return null; + public function parseDate($date) + { + $arr = explode(',', $date); + array_shift($arr); + return implode($arr); } - try { - $event = new Event([ - 'status' => 'APPROVED', - 'title' => trim($row['activity_title']), - 'slug' => str_slug(trim($row['activity_title'])), - 'organizer' => trim($row['name_of_organisation']), - 'description' => trim($row['description']), - 'organizer_type' => trim($row['type_of_organisation']), - 'activity_type' => trim($row['activity_type']), - 'location' => !empty($row['address']) ? trim($row['address']) : 'online', - 'event_url' => !empty($row['organiser_website']) ? trim($row['organiser_website']) : '', - 'contact_person' => !empty($row['contact_email']) ? trim($row['contact_email']) : '', - 'user_email' => '', - 'creator_id' => 132942, - 'country_iso' => trim($row['country']), - 'picture' => !empty($row['image_path']) ? trim($row['image_path']) : '', - 'pub_date' => now(), - 'created' => now(), - 'updated' => now(), - 'codeweek_for_all_participation_code' => 'cw20-coderdojo-eu', - 'start_date' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row['start_date']), - 'end_date' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row['end_date']), - 'geoposition' => (!empty($row['latitude']) && !empty($row['longitude'])) ? $row['latitude'] . ',' . $row['longitude'] : '', - 'longitude' => !empty($row['longitude']) ? trim($row['longitude']) : '', - 'latitude' => !empty($row['latitude']) ? trim($row['latitude']) : '', - 'language' => !empty($row['language']) ? trim($row['language']) : 'nl', - 'approved_by' => 19588, - 'mass_added_for' => 'Excel', - ]); - - $event->save(); - - if (!empty($row['audience_comma_separated_ids'])) { - $audiences = array_unique(array_map('trim', explode(',', $row['audience_comma_separated_ids']))); - $audiences = array_filter($audiences, function ($id) { - return is_numeric($id) && $id > 0 && $id <= 100; - }); - if (!empty($audiences)) { - $event->audiences()->attach($audiences); + public function model(array $row): ?Model + { + // Validate required fields + if ( + empty($row['activity_title']) || + empty($row['name_of_organisation']) || + empty($row['description']) || + empty($row['type_of_organisation']) || + empty($row['activity_type']) || + empty($row['country']) || + empty($row['start_date']) || + empty($row['end_date']) + ) { + Log::error('Missing required fields in row'); + return null; } - } - - if (!empty($row['theme_comma_separated_ids'])) { - $themes = array_unique(array_map('trim', explode(',', $row['theme_comma_separated_ids']))); - $themes = array_filter($themes, function ($id) { - return is_numeric($id) && $id > 0 && $id <= 100; - }); - if (!empty($themes)) { - $event->themes()->attach($themes); - } - } - return $event; - } catch (\Exception $e) { - Log::error('Event import failed: ' . $e->getMessage()); - return null; + try { + $event = new Event([ + 'status' => 'APPROVED', + 'title' => trim($row['activity_title']), + 'slug' => str_slug(trim($row['activity_title'])), + 'organizer' => trim($row['name_of_organisation']), + 'description' => trim($row['description']), + 'organizer_type' => trim($row['type_of_organisation']), + 'activity_type' => trim($row['activity_type']), + 'location' => !empty($row['address']) ? trim($row['address']) : 'online', + 'event_url' => !empty($row['organiser_website']) ? trim($row['organiser_website']) : '', + 'contact_person' => !empty($row['contact_email']) ? trim($row['contact_email']) : '', + 'user_email' => '', + 'creator_id' => 132942, + 'country_iso' => trim($row['country']), + 'picture' => !empty($row['image_path']) ? trim($row['image_path']) : '', + 'pub_date' => now(), + 'created' => now(), + 'updated' => now(), + 'codeweek_for_all_participation_code' => 'cw20-coderdojo-eu', + 'start_date' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row['start_date']), + 'end_date' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row['end_date']), + 'geoposition' => (!empty($row['latitude']) && !empty($row['longitude'])) ? $row['latitude'] . ',' . $row['longitude'] : '', + 'longitude' => !empty($row['longitude']) ? trim($row['longitude']) : '', + 'latitude' => !empty($row['latitude']) ? trim($row['latitude']) : '', + 'language' => !empty($row['language']) ? trim($row['language']) : 'nl', + 'approved_by' => 19588, + 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, + ]); + + $event->save(); + + if (!empty($row['audience_comma_separated_ids'])) { + $audiences = array_unique(array_map('trim', explode(',', $row['audience_comma_separated_ids']))); + $audiences = array_filter($audiences, function ($id) { + return is_numeric($id) && $id > 0 && $id <= 100; + }); + if (!empty($audiences)) { + $event->audiences()->attach($audiences); + } + } + + if (!empty($row['theme_comma_separated_ids'])) { + $themes = array_unique(array_map('trim', explode(',', $row['theme_comma_separated_ids']))); + $themes = array_filter($themes, function ($id) { + return is_numeric($id) && $id > 0 && $id <= 100; + }); + if (!empty($themes)) { + $event->themes()->attach($themes); + } + } + + return $event; + } catch (\Exception $e) { + Log::error('Event import failed: ' . $e->getMessage()); + return null; + } } - } } diff --git a/app/Imports/EventsImport.php b/app/Imports/EventsImport.php index a1c315d08..de16306ef 100644 --- a/app/Imports/EventsImport.php +++ b/app/Imports/EventsImport.php @@ -9,9 +9,8 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; -class EventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class EventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -56,6 +55,41 @@ public function model(array $row): ?Model 'longitude' => $row['longitude'], 'latitude' => $row['latitude'], 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/GenericEventsImport.php b/app/Imports/GenericEventsImport.php index f5e86c8a8..72d5403be 100644 --- a/app/Imports/GenericEventsImport.php +++ b/app/Imports/GenericEventsImport.php @@ -10,10 +10,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class GenericEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class GenericEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -64,6 +63,41 @@ public function model(array $row): ?Model 'latitude' => $row['latitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/HamburgEventsImport.php b/app/Imports/HamburgEventsImport.php index 2ef82da0c..022200d37 100644 --- a/app/Imports/HamburgEventsImport.php +++ b/app/Imports/HamburgEventsImport.php @@ -9,10 +9,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class HamburgEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class HamburgEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -49,6 +48,41 @@ public function model(array $row): ?Model 'longitude' => $row['longitude'], 'latitude' => $row['latitude'], 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/IrelandDreamSpaceImport.php b/app/Imports/IrelandDreamSpaceImport.php index 1dd8ebe24..adda2e679 100644 --- a/app/Imports/IrelandDreamSpaceImport.php +++ b/app/Imports/IrelandDreamSpaceImport.php @@ -5,14 +5,12 @@ use App\Event; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Facades\Log; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class IrelandDreamSpaceImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class IrelandDreamSpaceImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -61,6 +59,41 @@ public function model(array $row): ?Model 'language' => '', 'approved_by' => 19588, 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/IrelandEventsImport.php b/app/Imports/IrelandEventsImport.php index 5cb2b36ef..ab8a5b62b 100644 --- a/app/Imports/IrelandEventsImport.php +++ b/app/Imports/IrelandEventsImport.php @@ -4,17 +4,15 @@ use App\Event; use App\User; -use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class IrelandEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class IrelandEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { // public function parseDate($date, $time) { // $time = Date::excelToDateTimeObject($time); @@ -72,6 +70,41 @@ public function model(array $row): ?Model 'latitude' => str_replace(',', '.', $row['latitude']), 'language' => 'en', 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/LuxembourgEventsImport.php b/app/Imports/LuxembourgEventsImport.php index faaf2d34e..41649dfca 100644 --- a/app/Imports/LuxembourgEventsImport.php +++ b/app/Imports/LuxembourgEventsImport.php @@ -8,10 +8,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class LuxembourgEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class LuxembourgEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -47,6 +46,41 @@ public function model(array $row): ?Model 'latitude' => $row['latitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/MagentaEventsImport.php b/app/Imports/MagentaEventsImport.php index 256d0ac59..088d7858f 100644 --- a/app/Imports/MagentaEventsImport.php +++ b/app/Imports/MagentaEventsImport.php @@ -7,10 +7,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class MagentaEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class MagentaEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -46,6 +45,41 @@ public function model(array $row): ?Model 'latitude' => $row['latitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/ReportedEventsImport.php b/app/Imports/ReportedEventsImport.php index ee0b29534..3d2b0b2fc 100644 --- a/app/Imports/ReportedEventsImport.php +++ b/app/Imports/ReportedEventsImport.php @@ -10,10 +10,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class ReportedEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class ReportedEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -67,6 +66,41 @@ public function model(array $row): ?Model 'average_participant_age' => 10, 'percentage_of_females' => 50, 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/TelerikEventsImport.php b/app/Imports/TelerikEventsImport.php index 58b3bb9ba..5662cd663 100644 --- a/app/Imports/TelerikEventsImport.php +++ b/app/Imports/TelerikEventsImport.php @@ -8,10 +8,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class TelerikEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class TelerikEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -48,6 +47,41 @@ public function model(array $row): ?Model 'longitude' => $row['longitude'], 'latitude' => $row['latitude'], 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/UKDigitAllCharityEventsImport.php b/app/Imports/UKDigitAllCharityEventsImport.php index cfae5b1c8..1ec5fb084 100644 --- a/app/Imports/UKDigitAllCharityEventsImport.php +++ b/app/Imports/UKDigitAllCharityEventsImport.php @@ -8,10 +8,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class UKDigitAllCharityEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class UKDigitAllCharityEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -48,6 +47,41 @@ public function model(array $row): ?Model 'latitude' => $row['latitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/Imports/UKDigitAllEventsImport.php b/app/Imports/UKDigitAllEventsImport.php index 4811180ca..5f2041caf 100644 --- a/app/Imports/UKDigitAllEventsImport.php +++ b/app/Imports/UKDigitAllEventsImport.php @@ -8,10 +8,9 @@ use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithCustomValueBinder; use Maatwebsite\Excel\Concerns\WithHeadingRow; -use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Shared\Date; -class UKDigitAllEventsImport extends DefaultValueBinder implements ToModel, WithCustomValueBinder, WithHeadingRow +class UKDigitAllEventsImport extends BaseEventsImport implements ToModel, WithCustomValueBinder, WithHeadingRow { public function parseDate($date) { @@ -46,6 +45,41 @@ public function model(array $row): ?Model 'latitude' => $row['latitude'], 'language' => strtolower($row['language']), 'mass_added_for' => 'Excel', + 'recurring_event' => isset($row['recurring_event']) + ? $this->validateSingleChoice($row['recurring_event'], Event::RECURRING_EVENTS) + : null, + + 'males_count' => isset($row['males_count']) ? (int) $row['males_count'] : null, + 'females_count' => isset($row['females_count']) ? (int) $row['females_count'] : null, + 'other_count' => isset($row['other_count']) ? (int) $row['other_count'] : null, + + 'is_extracurricular_event' => isset($row['is_extracurricular_event']) + ? $this->parseBool($row['is_extracurricular_event']) + : false, + + 'is_standard_school_curriculum' => isset($row['is_standard_school_curriculum']) + ? $this->parseBool($row['is_standard_school_curriculum']) + : false, + + 'is_use_resource' => isset($row['is_use_resource']) + ? $this->parseBool($row['is_use_resource']) + : false, + + 'activity_format' => isset($row['activity_format']) + ? $this->validateMultiChoice($row['activity_format'], Event::ACTIVITY_FORMATS) + : [], + + 'ages' => isset($row['ages']) + ? $this->validateMultiChoice($row['ages'], Event::AGES) + : [], + + 'duration' => isset($row['duration']) + ? $this->validateSingleChoice($row['duration'], Event::DURATIONS) + : null, + + 'recurring_type' => isset($row['recurring_type']) + ? $this->validateSingleChoice($row['recurring_type'], Event::RECURRING_TYPES) + : null, ]); $event->save(); diff --git a/app/MeetAndCodeRSSItem.php b/app/MeetAndCodeRSSItem.php index bdff5460f..c8ac2afb6 100644 --- a/app/MeetAndCodeRSSItem.php +++ b/app/MeetAndCodeRSSItem.php @@ -60,6 +60,31 @@ */ class MeetAndCodeRSSItem extends Model { + protected $fillable = [ + 'activity_format', + 'duration', + 'recurring_event', + 'recurring_type', + 'males_count', + 'females_count', + 'other_count', + 'is_extracurricular_event', + 'is_standard_school_curriculum', + 'is_use_resource', + 'ages' + ]; + + protected function casts(): array + { + return [ + 'activity_format' => 'array', + 'is_extracurricular_event' => 'boolean', + 'is_standard_school_curriculum' => 'boolean', + 'ages' => 'array', + 'is_use_resource' => 'boolean', + ]; + } + public function getCountryIso() { @@ -73,7 +98,6 @@ public function getCountryIso() default: return Country::where('name', 'like', $this->country)->first()->iso; } - } private function mapOrganisationTypes($organisation_type) @@ -84,7 +108,6 @@ private function mapOrganisationTypes($organisation_type) default: return 'other'; } - } private function mapActivityTypes($activity_type) @@ -97,7 +120,6 @@ private function mapActivityTypes($activity_type) default: return 'other'; } - } public function createEvent($user) @@ -125,9 +147,20 @@ public function createEvent($user) 'end_date' => $this->end_date, 'longitude' => $this->lon, 'latitude' => $this->lat, - 'geoposition' => $this->lat.','.$this->lon, + 'geoposition' => $this->lat . ',' . $this->lon, 'language' => MeetAndCodeHelper::getLanguage($this->link), 'mass_added_for' => 'RSS meet_and_code', + 'activity_format' => is_array($this->activity_format) ? $this->activity_format : [], + 'duration' => $this->duration, + 'recurring_event' => $this->recurring_event, + 'recurring_type' => $this->recurring_type, + 'males_count' => $this->males_count, + 'females_count' => $this->females_count, + 'other_count' => $this->other_count, + 'is_extracurricular_event' => $this->is_extracurricular_event, + 'is_standard_school_curriculum' => $this->is_standard_school_curriculum, + 'ages' => is_array($this->ages) ? $this->ages : [], + 'is_use_resource' => $this->is_use_resource, ]); $event->save(); @@ -137,6 +170,5 @@ public function createEvent($user) $event->themes()->attach(8); return $event; - } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 31366704c..0d22c7105 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -9,9 +9,11 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\View; +use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; use Illuminate\Validation\Rules\Password; use Illuminate\Support\Facades\Blade; +use Illuminate\Support\Facades\Lang; class AppServiceProvider extends ServiceProvider { @@ -40,6 +42,7 @@ function ($view) { $view->with('audiences', \App\Audience::all()); $view->with('activity_types', \App\ActivityType::list()); $view->with('countries', \App\Country::translated()); + $view->with('languages', Arr::sort(Lang::get('base.languages'))); $view->with('active_countries', \App\Country::withEvents()); $view->with( 'themes', diff --git a/app/Queries/EventsQuery.php b/app/Queries/EventsQuery.php index 1894ae1d1..fd153e194 100644 --- a/app/Queries/EventsQuery.php +++ b/app/Queries/EventsQuery.php @@ -37,6 +37,13 @@ public static function store(Request $request) abort(503, 'Title error'); } + if (!isset($request['participants_count'])) { + $request['participants_count'] = (int) $request['males_count'] + (int) $request['females_count'] + (int) $request['other_count']; + if ($request['participants_count'] > 0) { + $request['percentage_of_females'] = number_format((int) $request['females_count'] / (int) $request['participants_count'] * 100, 2); + } + } + $request['pub_date'] = Carbon::now(); $request['created'] = Carbon::now(); $request['updated'] = Carbon::now(); @@ -62,6 +69,14 @@ public static function store(Request $request) $request['codeweek_for_all_participation_code'] = $codeweek_4_all_generated_code; } + if (!empty($request['activity_format']) && is_string($request['activity_format'])) { + $request['activity_format'] = explode(',', $request['activity_format']); + } + + if (!empty($request['ages']) && is_string($request['ages'])) { + $request['ages'] = explode(',', $request['ages']); + } + $event = Event::create($request->toArray()); if (! empty($request['tags'])) { @@ -101,11 +116,26 @@ public static function update(Request $request, Event $event) $request['latitude'] = explode(',', $request['geoposition'])[0]; $request['longitude'] = explode(',', $request['geoposition'])[1]; + if (!isset($request['participants_count'])) { + $request['participants_count'] = (int) $request['males_count'] + (int) $request['females_count'] + (int) $request['other_count']; + if ($request['participants_count'] > 0) { + $request['percentage_of_females'] = number_format((int) $request['females_count'] / (int) $request['participants_count'] * 100, 2); + } + } + //In order to appear again in the list for the moderators if ($event->status == 'REJECTED') { $request['status'] = 'PENDING'; } + if (!empty($request['activity_format']) && is_string($request['activity_format'])) { + $request['activity_format'] = explode(',', $request['activity_format']); + } + + if (!empty($request['ages']) && is_string($request['ages'])) { + $request['ages'] = explode(',', $request['ages']); + } + $event->update($request->toArray()); $request['theme'] = explode(',', $request['theme']); diff --git a/app/Queries/SuperOrganiserQuery.php b/app/Queries/SuperOrganiserQuery.php index ac8510d74..f10b52a90 100644 --- a/app/Queries/SuperOrganiserQuery.php +++ b/app/Queries/SuperOrganiserQuery.php @@ -20,7 +20,7 @@ public static function mine() public static function winners($edition) { $winners = DB::table('events') - ->where('status', '=', 'APPROVED') + ->where('status', 'APPROVED') ->whereNull('deleted_at') ->whereYear('end_date', '=', $edition) ->groupBy('creator_id') diff --git a/app/User.php b/app/User.php index 1f45713e1..f33e37b58 100644 --- a/app/User.php +++ b/app/User.php @@ -385,8 +385,8 @@ public function activities($edition) { return DB::table('events') - ->where('creator_id', '=', $this->id) - ->where('status', "=", "APPROVED") + ->where('creator_id', $this->id) + ->where('status', "APPROVED") ->whereNull('deleted_at') ->whereYear('end_date', '=', $edition) ->count(); @@ -397,7 +397,7 @@ public function reported($edition = null) $query = DB::table('events') ->where('creator_id', '=', $this->id) - ->where('status', "=", "APPROVED") + ->where('status', "APPROVED") ->whereNotNull('reported_at') ->whereNull('deleted_at'); @@ -429,7 +429,7 @@ public function influence($edition = null) // Log::info("$nameInTag - $edition not in cache"); $taggedActivities = $this->taggedActivities() - ->where('status', '=', 'APPROVED'); + ->where('status', 'APPROVED'); if (!is_null($edition)) { $taggedActivities->whereYear('events.created_at', '=', $edition); diff --git a/database/migrations/2025_05_06_162855_add_new_fields_for_new_form_to_events_table.php b/database/migrations/2025_05_06_162855_add_new_fields_for_new_form_to_events_table.php new file mode 100644 index 000000000..b7638eae4 --- /dev/null +++ b/database/migrations/2025_05_06_162855_add_new_fields_for_new_form_to_events_table.php @@ -0,0 +1,50 @@ +json('activity_format')->nullable(); + $table->string('duration')->nullable()->after('activity_format'); + $table->string('recurring_event')->nullable()->after('duration'); + $table->string('recurring_type')->nullable()->after('recurring_event'); + $table->integer('males_count')->nullable()->after('recurring_type'); + $table->integer('females_count')->nullable()->after('males_count'); + $table->integer('other_count')->nullable()->after('females_count'); + $table->boolean('is_extracurricular_event')->default(false)->after('other_count'); + $table->boolean('is_standard_school_curriculum')->default(false)->after('is_extracurricular_event'); + $table->json('ages')->nullable()->after('is_standard_school_curriculum'); + $table->boolean('is_use_resource')->default(false)->after('ages'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('events', function (Blueprint $table) { + $table->dropColumn([ + 'activity_format', + 'duration', + 'recurring_event', + 'recurring_type', + 'males_count', + 'females_count', + 'other_count', + 'is_extracurricular_event', + 'is_standard_school_curriculum', + 'ages', + 'is_use_resource', + ]); + }); + } +}; diff --git a/database/migrations/2025_05_15_102937_add_new_fields_for_new_form_to_meet_and_code_r_s_s_items_table.php b/database/migrations/2025_05_15_102937_add_new_fields_for_new_form_to_meet_and_code_r_s_s_items_table.php new file mode 100644 index 000000000..995db3c3c --- /dev/null +++ b/database/migrations/2025_05_15_102937_add_new_fields_for_new_form_to_meet_and_code_r_s_s_items_table.php @@ -0,0 +1,38 @@ +json('activity_format')->nullable(); + $table->string('duration')->nullable()->after('activity_format'); + $table->string('recurring_event')->nullable()->after('duration'); + $table->string('recurring_type')->nullable()->after('recurring_event'); + $table->integer('males_count')->nullable()->after('recurring_type'); + $table->integer('females_count')->nullable()->after('males_count'); + $table->integer('other_count')->nullable()->after('females_count'); + $table->boolean('is_extracurricular_event')->default(false)->after('other_count'); + $table->boolean('is_standard_school_curriculum')->default(false)->after('is_extracurricular_event'); + $table->json('ages')->nullable()->after('is_standard_school_curriculum'); + $table->boolean('is_use_resource')->default(false)->after('ages'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('meet_and_code_r_s_s_items', function (Blueprint $table) { + // + }); + } +}; diff --git a/public/images/activity/banner.png b/public/images/activity/banner.png new file mode 100644 index 000000000..4606ddf0d Binary files /dev/null and b/public/images/activity/banner.png differ diff --git a/public/images/check-white.svg b/public/images/check-white.svg new file mode 100644 index 000000000..2edf088d0 --- /dev/null +++ b/public/images/check-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icon_error.svg b/public/images/icon_error.svg new file mode 100644 index 000000000..de1cef492 --- /dev/null +++ b/public/images/icon_error.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icon_image.svg b/public/images/icon_image.svg new file mode 100644 index 000000000..cc27b798d --- /dev/null +++ b/public/images/icon_image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/marker-blue.svg b/public/images/marker-blue.svg new file mode 100644 index 000000000..69337484a --- /dev/null +++ b/public/images/marker-blue.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/marker-orange.svg b/public/images/marker-orange.svg new file mode 100644 index 000000000..a58a8c519 --- /dev/null +++ b/public/images/marker-orange.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/star.svg b/public/images/star.svg new file mode 100644 index 000000000..26db2ca91 --- /dev/null +++ b/public/images/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/assets/sass/app.scss b/resources/assets/sass/app.scss index 34fa24498..0e049f9ce 100644 --- a/resources/assets/sass/app.scss +++ b/resources/assets/sass/app.scss @@ -12,6 +12,7 @@ @import "components/pagination"; @import "components/calendar"; @import "components/table"; +@import "components/tinymce"; //Import pages @import "pages"; diff --git a/resources/assets/sass/components/forms.scss b/resources/assets/sass/components/forms.scss index e79471a5e..90d8e2ae9 100644 --- a/resources/assets/sass/components/forms.scss +++ b/resources/assets/sass/components/forms.scss @@ -1,4 +1,18 @@ +.custom-geo-input { + input { + width: 100%; + border: 2px solid #a4b8d9; + border-radius: 24px; + height: 48px; + font-size: 20px; + line-height: 28px; + padding: 0 16px; + color: #20262C; + } +} + .multiselect.multi-select { + &.multiselect--active { .multiselect--values { display: none; @@ -9,6 +23,7 @@ } .multiselect__tags{ + cursor: pointer; border-radius: 24px; min-height: 46px; border: 2px solid #A4B8D9; @@ -25,13 +40,13 @@ height: 100%; } .multiselect__placeholder, - .multiselect__single{ + .multiselect__tags .multiselect__single{ padding: 0; margin: 0; font-size: 16px; font-style: normal; font-weight: 400; - color: #333E48; + color: #20262C; } .multiselect__placeholder { max-width: 100%; @@ -41,6 +56,7 @@ text-overflow: ellipsis; line-height: 18px; } + .multiselect__single { padding-top: 6px; color: #333E48; @@ -54,7 +70,7 @@ height: 8px; width: 8px; border: none; - border-left: 2px solid #5F718A; + border-left: 2px solid #1C4DA1; transform: rotate(45deg) !important; } .multiselect__select:after { @@ -66,7 +82,7 @@ height: 8px; width: 8px; border: none; - border-left: 2px solid #5F718A; + border-left: 2px solid #1C4DA1; transform: rotate(-45deg); } .multiselect__tags-wrap{ @@ -78,6 +94,83 @@ margin: 0; } } + + &.large-text { + .multiselect__placeholder, + .multiselect__single{ + font-size: 20px; + line-height: 24px; + } + .multiselect__placeholder { + line-height: 24px; + } + .multiselect__input { + line-height: 24px; + } + .multiselect__tags{ + padding: 9px 40px 8px 16px; + min-height: 48px; + } + } + + &.new-theme { + .multiselect__placeholder { + display: block; + } + .multiselect__tags { + border-radius: 24px !important; + } + + .multiselect__content-wrapper { + border: 2px solid #ADB2B6; + border-radius: 12px; + } + + &.multiple .multiselect__content-wrapper { + overflow: hidden; + padding: 16px 12px 16px 0; + + .multiselect__content { + min-height: 1px; + max-height: calc(300px - 32px); + overflow: auto; + + &::-webkit-scrollbar { + border-radius: 6px; + width: 12px; + display: block; + } + + &::-webkit-scrollbar-track { + background: #E8EDF6; + border-radius: 8px; + } + + &::-webkit-scrollbar-thumb { + background: #1C4DA1; + border-radius: 6px; + } + } + } + + .multiselect__option { + padding-left: 24px; + font-size: 20px; + &.multiselect__option--highlight { + background-color: #eee; + color: #20262C; + } + &.multiselect__option--selected { + background-color: transparent; + &:hover { + background-color: #eee; + } + } + &:after { + display: none; + } + } + } } .multiselect .multiselect__tags{ diff --git a/resources/assets/sass/components/tinymce.scss b/resources/assets/sass/components/tinymce.scss new file mode 100644 index 000000000..518111cb8 --- /dev/null +++ b/resources/assets/sass/components/tinymce.scss @@ -0,0 +1,27 @@ +.custom-tinymce { + .tox-tinymce { + border: 2px solid #a4b8d9; + border-radius: 24px; + } + + .tox-editor-container { + .tox-menubar { + padding: 0 12px; + } + } + .tox-toolbar-overlord { + .tox-toolbar__primary { + .tox-toolbar__group:first-child { + padding-left: 12px; + } + .tox-toolbar__group:last-child { + padding-right: 12px; + } + } + } + + .tox .tox-statusbar { + height: 24px; + padding: 4px 16px 4px 16px; + } +} diff --git a/resources/excel/sample.xlsx b/resources/excel/sample.xlsx index b62539dbe..a351ec147 100644 Binary files a/resources/excel/sample.xlsx and b/resources/excel/sample.xlsx differ diff --git a/resources/js/app.js b/resources/js/app.js index 53c4e4f73..7ef78419b 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,5 +1,6 @@ import './bootstrap'; import { createApp } from 'vue'; +import ActivityForm from './components/activity-form/index.vue'; import ResourceForm from './components/ResourceForm.vue'; import ResourceCard from './components/ResourceCard.vue'; import ResourcePill from './components/ResourcePill.vue'; @@ -15,6 +16,8 @@ import PictureForm from "./components/PictureForm.vue"; import Flash from "./components/Flash.vue"; import InputTags from "./components/InputTags.vue"; import ReportEvent from "./components/ReportEvent.vue"; +import EventCard from "./components/EventCard.vue"; +import EventDetail from "./components/EventDetail.vue"; import SearchPageComponent from "./components/SearchPageComponent.vue"; // import { createI18n } from 'vue-i18n'; @@ -34,6 +37,7 @@ app.use(i18nVue, { return await langs[`../lang/${lang}.json`](); } }) +app.component('ActivityForm', ActivityForm); app.component('ResourceForm', ResourceForm); app.component('ResourceCard', ResourceCard); app.component('ResourcePill', ResourcePill); @@ -52,4 +56,6 @@ app.component('InputTags', InputTags); app.component('SearchPageComponent', SearchPageComponent); app.component('AvatarForm', AvatarForm); app.component('PartnerGallery', PartnerGallery); +app.component('EventCard', EventCard); +app.component('EventDetail', EventDetail); app.mount("#app"); diff --git a/resources/js/components/AutocompleteGeo.vue b/resources/js/components/AutocompleteGeo.vue index 38a9562e8..f3619d443 100644 --- a/resources/js/components/AutocompleteGeo.vue +++ b/resources/js/components/AutocompleteGeo.vue @@ -37,21 +37,26 @@ export default { geoposition: String, location: String }, - setup(props) { + emits: ['onChange'], + setup(props, { emit }) { const item = ref(props.value ? { name: props.value } : null); const items = ref(null); const template = ItemTemplate; - const inputAttrs = { + const inputAttrs = ref({ placeholder: props.placeholder, name: props.name, autocomplete: "off" - }; + }); const localGeoposition = ref(props.geoposition); const initialLocation = props.location; - + watch(() => props.placeholder, () => { + inputAttrs.value.placeholder = props.placeholder; + }); const itemSelected = (selectedItem) => { + emit('onChange', { location: selectedItem?.name || '' }); + if (selectedItem && selectedItem.name && selectedItem.magicKey) { const baseURL = "/api/proxy/geocode"; // Update to your Laravel endpoint axios.get(baseURL, { @@ -66,7 +71,16 @@ export default { window.map.setView([candidate.location.y, candidate.location.x], 16); } const countryIso2 = findCountry(candidate.attributes.Country).iso2; - document.getElementById('id_country').value = countryIso2; + + emit('onChange', { + location: selectedItem?.name || '', + geoposition: [candidate.location.y, candidate.location.x], + country_iso: countryIso2 || '', + }); + + if (document.getElementById('id_country')) { + document.getElementById('id_country').value = countryIso2; + } }).catch(error => { console.error('Error:', error); }); diff --git a/resources/js/components/DateTime.vue b/resources/js/components/DateTime.vue index f5a624413..6b13b9be6 100644 --- a/resources/js/components/DateTime.vue +++ b/resources/js/components/DateTime.vue @@ -1,7 +1,7 @@ @@ -11,7 +11,7 @@ import '@vuepic/vue-datepicker/dist/main.css' export default { components: { VueDatePicker }, - props: ['name','placeholder','value','lang'], + props: ['name','placeholder','value','lang', 'format', 'onClear'], data() { return { time1: this.value ? this.value : '', @@ -24,6 +24,21 @@ export default { } ] } + }, + methods: { + onChange(date) { + this.$emit('onClear'); + if (!(date instanceof Date) || isNaN(date.getTime())) return ''; + const pad = (n) => n.toString().padStart(2, '0'); + + const year = date.getFullYear(); + const month = pad(date.getMonth() + 1); + const day = pad(date.getDate()); + const hour = pad(date.getHours()); + const minute = pad(date.getMinutes()); + + this.$emit('onChange', `${year}-${month}-${day} ${hour}:${minute}`); + } } } @@ -32,12 +47,13 @@ export default { :deep(.dp__input) { border: none; --tw-ring-color: none; - color: #00000080; - font-family: "PT Sans"; - font-size: 16px; + color: #20262C; + font-family: "Blinker"; + font-size: 20px; font-style: normal; font-weight: 400; line-height: 24px; + background-color: transparent; } :deep(.dp__icon) { @@ -47,7 +63,7 @@ export default { :deep(.dp__menu_inner) { color: #00000080; - font-family: "PT Sans"; + font-family: "Blinker"; font-size: 16px; font-style: normal; font-weight: 400; diff --git a/resources/js/components/EventCard.vue b/resources/js/components/EventCard.vue new file mode 100644 index 000000000..adb9f321d --- /dev/null +++ b/resources/js/components/EventCard.vue @@ -0,0 +1,153 @@ + + + diff --git a/resources/js/components/EventDetail.vue b/resources/js/components/EventDetail.vue new file mode 100644 index 000000000..8ac0b9fef --- /dev/null +++ b/resources/js/components/EventDetail.vue @@ -0,0 +1,436 @@ + + + diff --git a/resources/js/components/Pagination.vue b/resources/js/components/Pagination.vue index 88e5e7676..83279bdd2 100644 --- a/resources/js/components/Pagination.vue +++ b/resources/js/components/Pagination.vue @@ -16,7 +16,7 @@
  • {{ page }} diff --git a/resources/js/components/SearchPageComponent.vue b/resources/js/components/SearchPageComponent.vue index 15afe7a7a..e9d64ea8b 100644 --- a/resources/js/components/SearchPageComponent.vue +++ b/resources/js/components/SearchPageComponent.vue @@ -1,285 +1,638 @@ diff --git a/resources/js/components/activity-form/AddConfirmation.vue b/resources/js/components/activity-form/AddConfirmation.vue new file mode 100644 index 000000000..bbc51972c --- /dev/null +++ b/resources/js/components/activity-form/AddConfirmation.vue @@ -0,0 +1,252 @@ + + + diff --git a/resources/js/components/activity-form/FormStep1.vue b/resources/js/components/activity-form/FormStep1.vue new file mode 100644 index 000000000..c3a095278 --- /dev/null +++ b/resources/js/components/activity-form/FormStep1.vue @@ -0,0 +1,292 @@ +