EcoSort, on the App Store

A photo of a printed bin calendar, read one month at a time

EcoSort shows which bins go out on your street and when. It reminds you the evening before, and the widget and the lock screen show what is next.

The hard part is the data

Every municipality publishes its schedule differently. Many publish a scan or a phone photo of a printed calendar: twelve months on one page, colour-coded days and a legend in the corner.

The obvious approach is to give a model the whole page and ask for the dates. On six hard scans the best version of that prompt got 24.5% of the cells wrong. A page holds hundreds of similar numbers, and where the model can't see each day clearly it fills in a regular pattern. In Wiśniew bio waste goes out once a month. The whole-page prompt returned it every week.

How the pipeline reads it

  1. Cut the page into monthsThe model marks the month frames, code moves the borders into the empty space between them, and a crop is rejected if its last visible day is wrong.
  2. Read each piece twiceTwo prompts read every month, with the legend attached. Tables get a second image with the columns spread apart.
  3. Break ties by majorityWhen the two reads disagree, a third read decides.
  4. Let code check what is certainSundays, public holidays, gaps in the rhythm, row counts, and that every address in the national register falls into exactly one zone.
  5. Hold back instead of guessingDoubt that the reads can't settle holds the zone back. A missing schedule is safer than a wrong date.

Results

Six scans and photos with 2,190 cells. The pipeline read each of them in three full runs, 6,570 cell results in all. A cell is one place, one waste type and one month, and it only counts as right if every day matches the document.

One prompt75.5% right, 24.5% wrong
Pipeline82.2% right, 17.8% held back, 0% wrong

Green is right, red is wrong, grey is held back. The ground truth is 966 decisions I made by hand in the admin panel. The pipeline was tuned on these documents, so zero wrong dates is its best case, on documents it already knows. Documents it has not seen are the next test.

DocumentCellsPrompt wrongPipeline wrongHeld back
Gierałtów, photo12057.2%00%
Czerna, photo11286.9%00%
Wykroty, photo12076.7%00%
Wiśniew, scan14777.8%033.3%
Szprotawa, sideways scan2641.0%074.2%
Klimontów, three-page scan of tables1,42711.3%010.1%
All six2,19024.5%017.8%

It is not better everywhere: in Szprotawa the prompt was nearly right and the pipeline held three quarters back. It also costs more: one call and about 25 seconds per document for the prompt, against a median of 33 calls and 4.5 minutes.

Open the Report demo

What I built

  • The iPhone app in SwiftUI, with widgets, a Live Activity, reminders you can snooze or mark as done, and a schedule that works offline.
  • An Android app in Kotlin and Jetpack Compose. It is built and not yet on Google Play.
  • The backend in FastAPI and PostgreSQL: address search against the national TERYT register, schedules served as static JSON.
  • The reading pipeline in Python with OpenCV, Tesseract, PyMuPDF and Gemini, and an admin panel in React and TypeScript.
  • Sortuś, an assistant that answers questions using the resident's own schedule.

Six municipalities are published in the app. EcoSort on the App Store. The website is ecosort.pl.

App Store screenshot: your collection scheduleApp Store screenshot: a countdown to each collectionApp Store screenshot: a reminder the evening beforeApp Store screenshot: the month calendarApp Store screenshot: widgetsApp Store screenshot: Sortuś the assistant