{"id":50647810,"url":"https://github.com/mtib/is_it_good","last_synced_at":"2026-06-07T14:02:32.876Z","repository":{"id":346883309,"uuid":"1192026984","full_name":"mtib/is_it_good","owner":"mtib","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-02T23:17:31.000Z","size":111,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-03T01:12:13.958Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://good.mtib.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtib.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-25T20:25:16.000Z","updated_at":"2026-06-02T23:17:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mtib/is_it_good","commit_stats":null,"previous_names":["mtib/is_it_good"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mtib/is_it_good","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtib%2Fis_it_good","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtib%2Fis_it_good/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtib%2Fis_it_good/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtib%2Fis_it_good/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtib","download_url":"https://codeload.github.com/mtib/is_it_good/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtib%2Fis_it_good/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34023902,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-07T14:02:32.195Z","updated_at":"2026-06-07T14:02:32.869Z","avatar_url":"https://github.com/mtib.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Is It Good?\n\nA web app that tells you how good each day is for outdoor activities based on weather forecasts. Get a subscribable calendar URL that rates upcoming days for biking, drone flying, and more.\n\n## Features\n\n- **Activity scoring** — Each activity has weather qualifiers (rain, wind, temperature, etc.) with weights. Days are scored 0-10 with labels: Bad, Poor, Fair, Good, Great.\n- **Subscribable calendars** — Generate an iCal URL you can add to Google Calendar, Apple Calendar, or any app that supports calendar subscriptions. Events update automatically as forecasts change.\n- **Graceful degradation** — Uses OpenWeatherMap's free 5-day forecast. If a paid API key is provided, extends to 16-day forecasts. Serves stale cached data if the API is unreachable.\n- **Aggressive caching** — Weather data is cached in SQLite (3h for 5-day, 6h for 16-day forecasts). Lat/lon rounded to ~1km precision to maximize cache hits.\n- **Calendar cutoff filter** — Optionally filter calendar events by score threshold (e.g., only show days scoring \u003e= 7) via query parameters on the `.ics` URL.\n- **Location-aware dates** — Weather data is bucketed by the location's local date, not UTC, so days align correctly regardless of timezone.\n\n## Activities\n\n| Activity | Key Qualifiers |\n|----------|---------------|\n| Biking | Rain, wind speed, temperature, humidity, cloud cover |\n| Drone Flying | Wind speed (heavily weighted), rain (very strict), visibility, temperature, cloud cover |\n| Running | Temperature (heavily weighted), rain, wind speed, humidity, cloud cover |\n\n## Quick Start\n\n```bash\n# Clone and install\nbun install\ncd frontend \u0026\u0026 bun install \u0026\u0026 cd ..\n\n# Set your OpenWeatherMap API key\ncp .env.example .env\n# Edit .env and add your OWM_API_KEY\n\n# Build frontend\nbun run build:frontend\n\n# Start server\nbun run start\n# → http://localhost:3000\n```\n\n## Development\n\n```bash\n# Backend with watch mode\nbun run dev\n\n# Frontend dev server (in another terminal)\nbun run dev:frontend\n# → http://localhost:5173 (proxies API to backend)\n```\n\n## Docker\n\n```bash\n# Set OWM_API_KEY in .env, then:\ndocker compose up\n# → http://localhost:3000\n```\n\n## API\n\n### Get available activities\n```\nGET /api/activities\n```\n\n### Search for a location\n```\nGET /api/geocode?q=Copenhagen\n```\n\n### Get scored forecast\n```\nGET /api/forecast?lat=55.68\u0026lon=12.57\u0026activity=biking\n```\n\n### Create calendar subscription\n```\nPOST /api/calendar/subscribe\nContent-Type: application/json\n\n{\"lat\": 55.68, \"lon\": 12.57, \"activity\": \"biking\", \"location_name\": \"Copenhagen\"}\n```\n\nReturns a URL like `/cal/abc123xyz.ics` — add this to your calendar app as a subscription.\n\nThe calendar URL supports optional cutoff filtering via query parameters:\n- `?cutoff=7\u0026mode=gte` — only include days scoring \u003e= 7\n- `?cutoff=3\u0026mode=lte` — only include days scoring \u003c= 3\n\n## How Scoring Works\n\nEach activity defines weather qualifiers with:\n- **Extract function** — pulls the relevant value from weather data\n- **Score function** — piecewise linear mapping from weather value to 0-10 score\n- **Weight** — relative importance of this qualifier\n\nThe overall day score is the weighted average of all qualifier scores. For example, drone flying weights wind at 6x and rain at 4x while temperature is only 1x, because drones are far more sensitive to wind and precipitation than to temperature. Drone wind scoring is strict — you need under 5 km/h to score 8+.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtib%2Fis_it_good","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtib%2Fis_it_good","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtib%2Fis_it_good/lists"}