diff --git a/app/Console/Commands/TrainingResourcesReport.php b/app/Console/Commands/TrainingResourcesReport.php index 2ad53dc95..b71055b39 100644 --- a/app/Console/Commands/TrainingResourcesReport.php +++ b/app/Console/Commands/TrainingResourcesReport.php @@ -2,6 +2,7 @@ namespace App\Console\Commands; +use App\OnlineCourse; use App\ResourceItem; use Carbon\Carbon; use Illuminate\Console\Command; @@ -24,14 +25,16 @@ class TrainingResourcesReport extends Command protected $signature = 'training:report {--format=text : Output format: text or json } {--baseline= : Override baseline date (Y-m-d) for "added since" } - {--learn-teach-only : Only output Learn & Teach resource counts (baseline + total now) }'; + {--learn-teach-only : Only output Learn & Teach resource counts (baseline + total now) } + {--online-courses-only : Only output Online Courses counts (baseline + total now) }'; - protected $description = 'Training resources report: counts (static training modules + Learn & Teach resources), baseline vs total. Download/geography/top10 require tracking or S3 logs.'; + protected $description = 'Training resources report: counts (static training modules + Learn & Teach + Online Courses), baseline vs total. Download/geography/top10 require tracking or S3 logs.'; public function handle(): int { $format = $this->option('format'); $learnTeachOnly = $this->option('learn-teach-only'); + $onlineCoursesOnly = $this->option('online-courses-only'); $baseline = $this->option('baseline') ? Carbon::parse($this->option('baseline')) : Carbon::parse(self::CODE4EUROPE_START); if ($learnTeachOnly) { @@ -50,11 +53,28 @@ public function handle(): int return self::SUCCESS; } + if ($onlineCoursesOnly) { + $stats = $this->onlineCoursesStats($baseline); + if ($format === 'json') { + $this->line(json_encode([ + 'online_courses' => $stats, + 'generated_at' => now()->toIso8601String(), + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + return self::SUCCESS; + } + $this->line('Online Courses (https://codeweek.eu/online-courses)'); + $this->line('Baseline date (project start): ' . $stats['baseline_date']); + $this->line('Total online courses now: ' . $stats['total_now']); + $this->line('Added since baseline: ' . $stats['added_since_baseline']); + return self::SUCCESS; + } + $report = [ 'generated_at' => now()->toIso8601String(), 'report_period_downloads' => 'June/Sept 2024 – Jan 2026 (requested). Download data not collected by application.', 'training_page' => $this->trainingPageStats(), 'learn_teach_resources' => $this->learnTeachResourcesStats($baseline), + 'online_courses' => $this->onlineCoursesStats($baseline), 'downloads' => $this->downloadsSection(), 'geographical_distribution' => $this->geographySection(), 'downloads_over_time' => $this->downloadsOverTimeSection(), @@ -97,6 +117,23 @@ private function learnTeachResourcesStats(Carbon $baseline): array ]; } + private function onlineCoursesStats(Carbon $baseline): array + { + $total = OnlineCourse::query()->where('active', true)->count(); + $addedSince = OnlineCourse::query() + ->where('active', true) + ->where('created_at', '>=', $baseline->copy()->startOfDay()) + ->count(); + + return [ + 'total_now' => $total, + 'added_since_baseline' => $addedSince, + 'baseline_date' => $baseline->format('Y-m-d'), + 'url' => 'https://codeweek.eu/online-courses', + 'note' => 'Online Courses (MOOCs) at /online-courses. Managed in Nova; counts use active records only.', + ]; + } + private function downloadsSection(): array { return [ @@ -158,6 +195,12 @@ private function printTextReport(array $report, Carbon $baseline): void $this->line($report['learn_teach_resources']['note']); $this->line(''); + $this->line('--- Online Courses (https://codeweek.eu/online-courses) ---'); + $this->line('Total online courses now: ' . $report['online_courses']['total_now']); + $this->line('Added since baseline (' . $baseline->format('Y-m-d') . '): ' . $report['online_courses']['added_since_baseline']); + $this->line($report['online_courses']['note']); + $this->line(''); + $this->line('--- Downloads ---'); $this->line('Total number: not tracked (links go to S3).'); $this->line('June/Sept 2024 – Jan 2026: not tracked.'); diff --git a/app/Http/Controllers/OnlineCoursesController.php b/app/Http/Controllers/OnlineCoursesController.php new file mode 100644 index 000000000..80f4602fc --- /dev/null +++ b/app/Http/Controllers/OnlineCoursesController.php @@ -0,0 +1,20 @@ +ordered()->get(); + + return view('online-courses', compact('onlineCourses')); + } +} diff --git a/app/Nova/OnlineCourse.php b/app/Nova/OnlineCourse.php new file mode 100644 index 000000000..2b0ec4873 --- /dev/null +++ b/app/Nova/OnlineCourse.php @@ -0,0 +1,55 @@ +sortable(), + Text::make('Title', 'title')->rules('required'), + Text::make('URL', 'url')->rules('required', 'url'), + Text::make('Date (display)', 'date_display') + ->help('e.g. "9 October – 15 November 2023"') + ->nullable(), + Text::make('Image', 'image') + ->help('Filename in /public/img/online-courses/ (e.g. navigating-innovative-technologies-across-the-curriculum.png)') + ->nullable(), + Textarea::make('Description', 'description')->nullable()->alwaysShow(), + Number::make('Position', 'position')->min(0)->help('Lower = higher on page.')->nullable(), + Boolean::make('Active', 'active'), + ]; + } + + public static function indexQuery(NovaRequest $request, $query) + { + return $query->ordered(); + } +} diff --git a/app/OnlineCourse.php b/app/OnlineCourse.php new file mode 100644 index 000000000..845c1dca1 --- /dev/null +++ b/app/OnlineCourse.php @@ -0,0 +1,36 @@ + 'boolean', + 'position' => 'integer', + ]; + + public function scopeActive($query) + { + return $query->where('active', true); + } + + public function scopeOrdered($query) + { + return $query->orderBy('position')->orderBy('created_at', 'desc'); + } +} diff --git a/database/migrations/2026_02_09_120000_create_online_courses_table.php b/database/migrations/2026_02_09_120000_create_online_courses_table.php new file mode 100644 index 000000000..e223ac1ff --- /dev/null +++ b/database/migrations/2026_02_09_120000_create_online_courses_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('title'); + $table->string('url'); + $table->string('date_display')->nullable(); // e.g. "9 October – 15 November 2023" + $table->string('image')->nullable(); // filename in /img/online-courses/ + $table->text('description')->nullable(); + $table->unsignedInteger('position')->default(0)->nullable(); + $table->boolean('active')->default(true); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('online_courses'); + } +}; diff --git a/database/seeders/OnlineCourseSeeder.php b/database/seeders/OnlineCourseSeeder.php new file mode 100644 index 000000000..275ef8426 --- /dev/null +++ b/database/seeders/OnlineCourseSeeder.php @@ -0,0 +1,112 @@ + 'Navigating Innovative Technologies Across the Curriculum', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+NavigatingTech+2023/about', + 'date_display' => '9 October – 15 November 2023 ', + 'image' => 'navigating-innovative-technologies-across-the-curriculum.png', + 'description' => 'The online course Navigating Innovative Technologies Across the Curriculum welcomes educators interested in integrating coding, computational thinking, virtual and augmented reality into their classrooms through interdisciplinary and cross-curricular projects, encouraging exploration of innovative technologies and their effective use.', + 'position' => 0, + 'created_at' => '2023-10-01', + ], + [ + 'title' => 'Unlocking the Power of AI in Education', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+AI_Education+2023/about', + 'date_display' => '13 March – 19 April 2023', + 'image' => 'unlocking-the-power-of-ai.png', + 'description' => "The Unlocking the Power of AI in Education MOOC aims to provide teachers with a basic understanding of AI's potentials and challenges in education, safe and effective use of educational data, ethical implications of AI in the classroom and the implementation of AI-related resources and materials in teaching practice.", + 'position' => 1, + 'created_at' => '2023-03-01', + ], + [ + 'title' => 'EU Code Week Bootcamp 2022', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+Bootcamp+2022/about', + 'date_display' => '10 October – 16 November 2022', + 'image' => 'eu-code-week-bootcamp-2022.jpg', + 'description' => 'The EU Code Week Bootcamp is designed for teachers who want to integrate coding and computational thinking into their classrooms with practical ideas and resources, while promoting diversity and inclusion in coding and exploring the potentials of artificial intelligence in education.', + 'position' => 2, + 'created_at' => '2022-10-01', + ], + [ + 'title' => 'EU Code Week Online Bootcamp', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+OnlineBootcamp+2021/about', + 'date_display' => '27 September – 27 October 2021', + 'image' => 'eu-code-week-online-bootcamp.jpg', + 'description' => 'The EU Code Week Online Bootcamp introduces teachers to the EU Code Week initiative and the opportunities it offers. Pre-primary, primary, and secondary school teachers are provided with practical ideas, tools, and resources for incorporating coding and computational thinking into the classroom.', + 'position' => 3, + 'created_at' => '2021-09-01', + ], + [ + 'title' => 'AI Basics for Schools', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+AI+2021/about', + 'date_display' => '8 March – 7 April 2021', + 'image' => 'ai-basics-for-schools.jpg', + 'description' => 'The course introduces teachers to the basic concepts of AI. It provides them with examples of effective use of AI in the classroom and supports them in the integration and creation of AI-related activities in the classroom. Teachers learn to recognize and raise awareness of threats and benefits of AI and examine the ethical use of AI.', + 'position' => 4, + 'created_at' => '2021-03-01', + ], + [ + 'title' => 'EU Code Week Deep Dive MOOC 2020', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+CWDive+2020/about', + 'date_display' => '16 September – 20 October 2020', + 'image' => 'eu-code-week-deep-dive-mooc-2020.jpg', + 'description' => 'The course aims to raise awareness about integrating coding and computational thinking into the classroom, familiarize teachers with innovative tools and approaches, offer free training materials in 29 languages, teach how to register activities for the EU Code Week initiative, explore the Code Week 4 all challenge and foster a community of enthusiastic teachers for best practice sharing.', + 'position' => 5, + 'created_at' => '2020-09-01', + ], + [ + 'title' => 'EU Code Week Icebreaker Rerun', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+Icebreaker+2020/about', + 'date_display' => '11 May – 15 June 2020', + 'image' => 'icebreaker.jpg', + 'description' => 'This short introductory course aims to make EU Code Week more appealing and meaningful for teachers, schools, and parents while raising awareness about the importance of integrating coding and computational thinking into their lesson. Learning how to code can empower students to be at the forefront of a digitally competent society, develop a better understanding of the world that surrounds them, and increase their chances to succeed in their personal and professional lives.', + 'position' => 6, + 'created_at' => '2020-05-01', + ], + [ + 'title' => 'EU Code Week - Deep Dive MOOC', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+CWDive+2019/about', + 'date_display' => '16 September – 30 October 2019', + 'image' => 'eu-code-week-deep-dive-mooc-2019.png', + 'description' => 'The course aims to help participants understand the importance of integrating coding and computational thinking in the classroom, become familiar with innovative tools and approaches, gain basic knowledge of the Code.org CS fundamentals curriculum, access free training materials and resources, explore the EU Code Week campaign, including activity registration and reporting.', + 'position' => 7, + 'created_at' => '2019-09-01', + ], + [ + 'title' => 'EU Code Week - Ice-breaker MOOC', + 'url' => 'https://www.europeanschoolnetacademy.eu/courses/course-v1:CodeWeek+Icebreaker+2019/about', + 'date_display' => '3 – 26 June 2019', + 'image' => 'icebreaker-2019.png', + 'description' => 'The course aims to introduce participants to EU Code Week and familiarize with the new EU Code Week website, explore and access free lesson plans to register activities for EU Code Week, discover various classroom support resources, and become acquainted with the toolkit designed for teachers.', + 'position' => 8, + 'created_at' => '2019-06-01', + ], + ]; + + foreach ($courses as $course) { + $createdAt = $course['created_at'] ?? null; + unset($course['created_at']); + $attrs = array_merge($course, ['active' => true]); + $model = OnlineCourse::firstOrCreate(['url' => $course['url']], $attrs); + if ($createdAt && $model->wasRecentlyCreated) { + $model->created_at = Carbon::parse($createdAt); + $model->save(); + } + } + } +} diff --git a/resources/views/online-courses.blade.php b/resources/views/online-courses.blade.php index d3a3a4427..a030e533f 100644 --- a/resources/views/online-courses.blade.php +++ b/resources/views/online-courses.blade.php @@ -88,86 +88,26 @@ class="animation-element move-background duration-[1.5s] absolute z-0 lg:-bottom