Drive Calendar

Drive Calendar is a custom-built application designed to automatically synchronize my driving lesson schedule with my digital calendar. The primary objective was to ensure I never missed a lesson while also tracking my progress across key driving skills such as steering, braking, and anticipation.

This project was inspired by my grandmother, who was a constant source of support throughout my driving journey. She loved staying updated on my lessons and progress, which motivated me to develop an easy-to-share tool for both myself and my loved ones.

Project Overview

The driving school's online dashboard was divided into two separate pages: one displaying upcoming lessons, and another detailing skill progression evaluations provided by the instructor after each lesson.

Data Extraction Approach

To retrieve this information, I developed a backend service that fetches both pages and parses their content using cheerio, a fast and flexible HTML parser for Node.js.

By spoofing the user-agent header to mimic a mobile device, I accessed simplified mobile versions of these pages, which significantly streamlined the scraping process.

Lesson Schedule

From the schedule page, I extracted all upcoming lessons, including timestamps and lesson numbers. This data was transformed into a structured JSON format:


[
  { lesson: 1, start: 1692606000, end: 1692609600 },
  { lesson: 2, start: 1692692400, end: 1692696000 },
  // ...
]
  

To create the live .ics calendar feed, I used ical-generator, a Node.js library that makes it simple to build and serve iCalendar files programmatically for integration with Google Calendar, Apple Calendar, and Outlook.

Calendar Integration Endpoints

To facilitate easy subscription, I implemented dedicated backend routes that redirect users directly to their preferred calendar service's import page, preloaded with the correct calendar feed URL:


app.get("/calendar/add/google", (req, res) => {
  res.redirect(`https://calendar.google.com/calendar/u/0/r?cid=webcal://${BASE_URL}/calendar.ics`);
});

app.get("/calendar/add/outlook/office", async (req, res) => {
  res.redirect(`https://outlook.office.com/calendar/addfromweb?url=https://${BASE_URL}/calendar.ics&name=Dinands Rijlessen`);
});

app.get("/calendar/add/outlook/live", async (req, res) => {
  res.redirect(`https://outlook.live.com/calendar/addfromweb?url=https://${BASE_URL}/calendar.ics&name=Dinands Rijlessen`);
});

app.get("/calendar/add/apple", async (req, res) => {
  res.redirect(`webcal://${BASE_URL}/calendar.ics`);
});
  

Skill Progression Tracking

From the progression page, I parsed detailed skill evaluations entered by the driving instructor after each lesson. These included metrics like braking, anticipation, and clutch control, each rated on a numeric scale.

The data structure I used to represent this was:


[
  {
    fase: "Traffic Participation",
    bases: [
      { base: "Steering", level: 6 },
      { base: "Braking", level: 5 },
      { base: "Anticipation", level: 4 },
    ]
  },
  {
    fase: "Vehicle Control",
    bases: [
      { base: "Clutch Control", level: 7 },
      { base: "Hill Start", level: 6 }
    ]
  }
]
  

Selecting appropriate naming conventions was a challenge. After considering terms like “skills,” “competencies,” and “metrics,” I adopted fase and base to stay consistent with the driving school's original terminology.

Below is a snapshot of how this progression data is displayed within the app interface:

Technology Stack

  • Node.js — Backend JavaScript runtime
  • Express — Web framework for Node.js
  • Cheerio — Fast, flexible HTML parsing for scraping
  • ical-generator — Programmatic iCalendar feed creation
  • JavaScript (ES6+) — Core language used throughout
  • HTML/CSS — Frontend markup and styling

Through this project, I gained valuable experience in web scraping, integrating external applications, and designing solutions with empathy. Ultimately, creating something simple yet meaningful for someone I care about was the most rewarding outcome.