Personal project

Greeney Transportation Carbon-Footprint Tracker

Next.js, Flask, Google Maps, Gmail API, Google OAuth, three.js, gunicorn

Figure 1 | The Greeney dashboard. The left column accepts per-mode inputs (or imports them from the user's Gmail receipts); the middle column breaks the resulting kilograms of CO₂ down by source; the right column translates the same number into more tangible quantities (the trees a user would need to plant to offset their monthly emissions, the equivalent miles in a personal vehicle, and an offset-cost estimate).

Greeney is a transportation carbon-footprint tracker built at BeaverHacks Spring 2025 (Oregon State University, 36 hours, hosted by the OSU Google Developer Group Club). Five transportation categories share one pipeline: Uber rides, Lyft rides, Uber Eats deliveries, DoorDash deliveries, and air flights. The user can enter rides directly through a form, or connect a Gmail account so the backend reads delivery and flight receipts automatically and extracts the relevant fields (restaurant or delivery address for ground orders, IATA airport codes for flights). Distances flow through Google Maps (Distance Matrix for ground travel, Geocoding for airport-coordinate lookups), and a per-mode emission factor turns those distances into kilograms of carbon dioxide.

I worked with three teammates on a four-person team. My half was the backend: the Flask app, the emissions calculator, the Google Maps and Gmail OAuth integrations, and the deployment wiring. The frontend, the 3D flight globe, and the team's branding were owned by my teammates. We took first place in the Data-Driven Solutions track.

Why this is interesting

Most carbon-footprint tools ask the user to remember and re-enter every trip. The friction of doing that consistently is what kills daily use, so most trackers stop being opened a week after sign-up. Greeney's design choice was to skip that friction entirely for the categories where the data already exists in the user's inbox. Uber Eats sends a receipt for every order. DoorDash sends one too. Airlines send confirmations with origin and destination IATA codes. Reading those emails is enough to reconstruct a meaningful share of someone's transportation footprint without the user having to log anything by hand.

The technical interest, from my side, was the chain of small things that all have to work for that pitch to land in 36 hours: a working Gmail OAuth flow, a small library of receipt-format regexes that hold up to Uber Eats's and DoorDash's formatting drift, a Google Maps integration that turns extracted strings into kilometers, and an emissions calculator with defensible per-mode factors.

The emissions model

The implemented formula is straightforward: kilograms of carbon dioxide equals miles times the per-mode factor. The factors are constants (0.40 kg per mile for Uber, Lyft, and food delivery, 0.25 kg per mile for flights), with backend/calculator.py as the source of truth. Tree-equivalence math uses 22 kilograms of carbon dioxide sequestered per tree per year, so the dashboard's "trees needed" tile is just the user's monthly footprint divided by 12 and again by 22. A 3,500-mile London-to-New-York reference distance is used in the about-page copy as a comparison point.

The conceptual framing of the model is grounded in Lannelongue, Grealey, Inouye 2021 (Advanced Science 2021), the Green Algorithms paper, which defines carbon footprint as carbon intensity times energy consumed. The hackathon implementation specializes that to "miles times factor" because a per-mode factor is a workable proxy for "intensity times consumption" when the workload is transportation and the per-mode behavior is roughly linear in distance. The factors themselves are back-of-envelope industry averages that we picked to be defensible during a 36-hour build, not a research contribution.

Gmail receipt parsing

The Gmail integration sits in backend/quickstart.py, adapted from the Google Gmail API Python quickstart. The app requests gmail.readonly scope through Google's OAuth flow, caches a refresh token locally, and pulls the most recent matching emails by sender. Each receipt class has its own regex extractor: Uber Eats receipts have a You ordered from <restaurant> line plus a Delivered to <address> block; DoorDash receipts have Order Confirmation for <user> from <restaurant> plus a delivery-address paragraph; airline confirmations have IATA airport codes that the extractor pulls with a tighter pattern.

The initial Google Maps integration plus the full emissions calculator shipped as PR-9 on the upstream repository. The pull request added about 1,100 lines: emission logic for all five modes with documented per-mile factors, the Distance Matrix integration for Uber Eats and DoorDash (origin to nearest restaurant) and flights (IATA to IATA), the geocoding helpers with fallback handling when no result returned, the Flask routes for the form and the /calculate JSON endpoint, and a setup walkthrough so my teammates could provision their own Google Maps API keys.

The restaurant-matching fix and the earth-radius bug

The first end-to-end test surfaced two issues. First, the Uber Eats and DoorDash regex extractors were producing restaurant-name strings that did not match anything in places/nearbySearch. The root cause was small but persistent: trailing newlines, the article "the" appearing inconsistently, and double spaces inside the captured names. The fix was a normalization pass before geocoding (trim, collapse whitespace, drop the leading article) so the search query landed on a real Google Places result. Shipped as part of PR-12, which also unified the flight input format across the form-entered and Gmail-parsed paths so the segment-walker did not need to branch, replaced status-500 tracebacks on missing fields with descriptive status-400 responses, and added an end-to-end test that exercises every mode through the Flask API.

Second, the flight-distance numbers looked off on the 3D globe. The Haversine formula in calculate_flight_distance was using a wrong constant for Earth's radius, which compressed long flights and exaggerated short ones. The fix is one line (the right value is 3,958.8 statute miles, or 6,371 kilometers depending on the unit carried through the formula), but tracking it down required laying the calculator output next to a known reference, which is what made the London-to-New-York 3,500-mile constant useful as a smoke check.

Reading list