Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions app/Console/Commands/TrainingResourcesReport.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Console\Commands;

use App\OnlineCourse;
use App\ResourceItem;
use Carbon\Carbon;
use Illuminate\Console\Command;
Expand All @@ -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) {
Expand All @@ -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(),
Expand Down Expand Up @@ -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 [
Expand Down Expand Up @@ -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.');
Expand Down
20 changes: 20 additions & 0 deletions app/Http/Controllers/OnlineCoursesController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Controllers;

use App\OnlineCourse;
use Illuminate\View\View;

class OnlineCoursesController extends Controller
{
/**
* Show the Online Courses page (https://codeweek.eu/online-courses).
* Courses are managed in Nova; displayed in order of position then created_at.
*/
public function index(): View
{
$onlineCourses = OnlineCourse::active()->ordered()->get();

return view('online-courses', compact('onlineCourses'));
}
}
55 changes: 55 additions & 0 deletions app/Nova/OnlineCourse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace App\Nova;

use Illuminate\Http\Request;
use Laravel\Nova\Fields\Boolean;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Number;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Textarea;
use Laravel\Nova\Http\Requests\NovaRequest;

class OnlineCourse extends Resource
{
public static $group = 'Resources';

public static $model = \App\OnlineCourse::class;

public static $title = 'title';

public static $search = ['title', 'description'];

public static function label()
{
return 'Online Courses';
}

public static function singularLabel()
{
return 'Online Course';
}

public function fields(Request $request): array
{
return [
ID::make()->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();
}
}
36 changes: 36 additions & 0 deletions app/OnlineCourse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class OnlineCourse extends Model
{
use HasFactory;

protected $fillable = [
'title',
'url',
'date_display',
'image',
'description',
'position',
'active',
];

protected $casts = [
'active' => 'boolean',
'position' => 'integer',
];

public function scopeActive($query)
{
return $query->where('active', true);
}

public function scopeOrdered($query)
{
return $query->orderBy('position')->orderBy('created_at', 'desc');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('online_courses', function (Blueprint $table) {
$table->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');
}
};
112 changes: 112 additions & 0 deletions database/seeders/OnlineCourseSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace Database\Seeders;

use App\OnlineCourse;
use Carbon\Carbon;
use Illuminate\Database\Seeder;

/**
* Seeds the online_courses table with the courses that were previously hardcoded
* in resources/views/online-courses.blade.php. Run after migrating.
*/
class OnlineCourseSeeder extends Seeder
{
public function run(): void
{
$courses = [
[
'title' => '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();
}
}
}
}
Loading
Loading