Brio // AmartaBack to portfolio

Case study / 02

PM2.5 Forecasting Platform

I built an end-to-end forecasting system for Jakarta as my Apple Developer Academy Solo Challenge. The goal went beyond training a model: I wanted to understand how data collection, evaluation, deployment, monitoring and a useful iOS experience fit into one reliable MLOps loop.

GitHub Actions history showing hourly PM2.5 forecasts and weekly model evaluation workflows
Automated MLOps / Jakarta
PM_24H / 2026
Context
Solo challenge
Location
Jakarta
Forecast
24 hours
Cadence
Hourly
Retraining
Weekly
My role
End to end

01 / Learning

MLOps is the work around the model.

This project changed how I think about machine learning. Reliability comes from the surrounding system: repeatable ingestion, traceable predictions, evaluation gates, monitoring, secrets and well-defined interfaces.

I also explored an optional Google Cloud Run deployment path and gained exposure to AWS-hosted infrastructure through Supabase, while staying precise about the implementation: the current product does not directly manage EC2, Lambda or S3.

02 / The challenge

A model is only one part of the product.

I designed the project around a practical question: how can a forecast be collected, produced before the real value is known, scored honestly later, improved safely and finally presented to someone in a useful way?

01 / Product goal

From experiment to operating system

Instead of stopping at a notebook, I connected the model to scheduled ingestion, persistent storage, evaluation gates, monitoring and two client experiences. Every component had to agree on timestamps, horizons, schemas and failure states.

02 / Constraint

Free-tier first

I chose services that kept the platform affordable and reproducible: Open-Meteo and CAMS for weather and air-quality data, Supabase for PostgreSQL storage and REST views, and GitHub Actions for scheduled execution.

03 / Responsibility

Owning the full loop

I worked across Python data processing, feature engineering, Ridge Regression, database design, scheduled workflows, secrets, FastAPI and Docker packaging, monitoring, evaluation logic, JSON contracts and the SwiftUI dashboard. That breadth was the core learning outcome of the project.

03 / Pipeline

Forecast first. Judge later.

The pipeline was designed to preserve the integrity of evaluation. A prediction is stored before its matching actual value exists; only after the observation arrives can the system calculate how well the forecast performed.

01 / Ingest

Collect

An hourly GitHub Actions workflow installs the project dependencies and runs the Python pipeline to collect the newest weather and PM2.5 observations.

02 / Forecast

Predict

The active Ridge Regression model builds historical features and writes the next 24 hours of forecasts to Supabase before the corresponding actuals arrive.

03 / Evaluate

Match

When matching observations become available, the system joins forecast and actual values, then calculates MAE and error by forecast horizon.

04 / Promote

Gate

A weekly workflow trains a candidate model. It becomes active only when it beats both the baseline and the currently deployed model.

04 / iOS product

Operational data, made readable.

The native app consumes four protected Supabase views through URLSession. I organized the experience around what a user needs to know now, what the system expects next and whether those expectations are trustworthy.

01 / Overview

Current conditions & pipeline health

The overview pairs the latest PM2.5 reading with system freshness and pipeline status, so a number is never shown without context about where it came from.

02 / Forecast

The next 24 hours

Swift Charts turns hourly predictions into a scannable timeline, helping the user understand the direction and shape of upcoming air-quality changes.

03 / Accuracy

Evidence behind the model

The accuracy view exposes MAE, a CAMS comparison and error by horizon. Model performance is part of the interface rather than a hidden engineering metric.

04 / Contract

Backend and client agreement

Stable JSON contracts keep the Python pipeline, protected database views and Swift types aligned. Designing those boundaries was as important as choosing the algorithm.

From forecast to feedback loop.