SwachhLens is a two-day hackathon project for turning citizen waste evidence into an explainable municipal cleanup response.
The current demo contains:
- Java 17 and Spring Boot 3 backend
- React, Vite, and Tailwind CSS frontend
- Responsive Citizen, Cleanup Team Leader, Municipal Officer, and State Officer interfaces
- Spring Security bearer-token authentication with BCrypt password hashing and backend role enforcement
- A deterministic municipal workflow backend with durable local demo storage
- Complaint fusion, explainable priority scoring, dispatch recipes, and task transitions
- Backend-only Gemini image perception with a deterministic no-key fallback
- Gemini before-versus-after cleanup comparison with mandatory Municipal Officer verification and citizen reopening feedback
- An Odisha-wide persisted hotspot and cleanup-performance dashboard covering all 30 district service areas
- Server-bound perception analysis IDs and mandatory supervisor review gates
- Server-bound after-cleanup evidence required for worker completion and officer closure
- Live team and vehicle availability across dispatch and verified closure
- Key-free Google Maps location links for officers and workers
- H2-backed prototype accounts and durable operational records; short-lived analysis, location-resolution and session-token stores remain bounded
No production database, Supabase integration, cloud image storage, real SMS/email delivery, or Google Maps API is active. The local H2 file preserves reports, incidents, tasks, teams and privacy-processed evidence across backend restarts.
The hackathon prototype uses a user-owned embedded H2 file for seeded/registered accounts and durable operational snapshots: reports, incidents, priority results, tasks, team/vehicle availability, submitted-image copies and after-cleanup evidence. The database starts with zero complaints, zero assigned tasks and zero hotspot points; preconfigured Odisha accounts and teams are configuration data. Short-lived perception-analysis IDs, Google Maps resolution IDs and bearer sessions intentionally expire and are not restart-persistent. PostgreSQL, MySQL, Supabase, Firebase, and other external databases are not connected. No database setup is required to run the demo profile.
A production deployment would use PostgreSQL or MySQL for Report, Incident, PriorityAssessment, Team, Vehicle, CleanupTask, OfficerDecision, IncidentEvent, and citizen status-history records. Indexes would support incident location/time lookup, task-status filtering, and priority queues. Secure object storage would hold citizen submission images and after-cleanup evidence after required privacy processing; the database would store metadata, access rules, integrity hashes, retention state, and storage references instead of unnecessary image copies.
Validated AI perception records would link to reports with confidence, flags, analysis timestamp, source, and human-review status. Because the Spring Boot code is organized by feature and REST contracts isolate the frontend from storage details, the local snapshot store can later be replaced with normalized Spring Data JPA repositories without changing the React workflow. Production controls would include role-based evidence access, retention/deletion policies, EXIF stripping, audited access, and keeping the Gemini key out of source control and all frontend code.
The tracked template contains placeholders only. Create or refresh your private local file from PowerShell:
cd backend
Copy-Item .env.example .envOpen backend/.env locally and set GEMINI_API_KEY to your key. Keep GEMINI_MODEL=gemini-3.5-flash unless you intentionally want another compatible model. Never paste the key into frontend files, logs, screenshots, chat, or a commit. The .env file is ignored by Git and is read only by Spring Boot.
With a blank key, the app runs in deterministic demo mode. If Gemini is unavailable or returns an invalid response, SwachhLens automatically returns a clearly labelled MOCK_FALLBACK result that requires human review. A live result that confidently marks the image CLEARLY_UNRELATED is rejected before an analysis ID or municipal incident is created; ambiguous and fallback results remain human-review gated.
Every successful perception response includes an opaque analysisId. The backend retains only the validated findings and expiry metadata in a bounded in-memory store; original image bytes are not retained. An analysis ID expires after 15 minutes and can be used once. Report submission derives all AI-related fields from that server record, so browser-supplied category, volume, risk, confidence, or review values are never trusted.
The Citizen Report page never requests location automatically. Select Use my current location to trigger the browser permission flow. A successful reading is labelled Current GPS and shows the browser-provided accuracy estimate. If permission is denied, location services are unavailable, the request times out, or the page is not running in a secure context, the page clearly reports that no GPS location was captured.
If the browser reading is unavailable, inaccurate, or not the waste location, the citizen can open Google Maps in a new tab, search or drop a pin, share it, and paste the copied Google Maps link into SwachhLens. No Google Maps API, Cloud project, billing account, or API key is used. The backend accepts HTTPS links only from google.com, www.google.com, maps.google.com, maps.app.goo.gl, or goo.gl; it follows only approved Google redirects with strict timeout, redirect, and response-size bounds. It extracts and validates the exact pin coordinates, returns an opaque short-lived location selection ID to the browser, and retains the original approved Maps URL with the durable report. Arbitrary URLs are never fetched.
The citizen UI labels a successful choice Google Maps location selected and does not display raw coordinates. Google short-link resolution depends on internet access and Google Maps availability; a final approved Google Maps URL that already contains exact coordinates does not require a redirect lookup. If a link does not contain an exact dropped pin, the UI asks the citizen to share a dropped pin and retry. The deterministic coordinates are used only after Use demo location is explicitly selected and are always labelled Demo location, never GPS. Report submission stays disabled until image analysis succeeds and Current GPS, a resolved Google Maps shared pin, or the explicit demo location is selected. Browsers generally allow GPS location on HTTPS pages and on localhost during development.
Coordinates and the automatically displayed captured time are sent only to the SwachhLens backend for the incident workflow. Accepted report coordinates are stored in the local H2 demo database so district routing, fusion, dispatch and history continue after a restart. The frontend does not retain a pending location after the page is reset.
- Java 17
- Node.js 20.19+ or 22.12+
- npm
The Maven wrapper is included, so a separate Maven installation is not required.
Open PowerShell in the repository root:
cd backend
$env:JAVA_HOME = 'C:\Program Files\Java\jdk-17'
.\mvnw.cmd spring-boot:run '-Dspring-boot.run.profiles=demo'The API starts at http://localhost:8080. Verify it with:
Invoke-RestMethod http://localhost:8080/api/healthExpected response:
{
"status": "UP",
"service": "SwachhLens backend is running"
}The demo profile requires no external database connection. It starts an embedded H2 file, seeds approved demo identities, sensitive locations, four Khordha teams plus one baseline team in each of the other 29 Odisha district service areas, and their vehicles. It deliberately seeds no complaints, incidents, assigned tasks or hotspot points.
These non-sensitive credentials exist only in the local demo profile:
| Role | Sign-in identifier | Password |
|---|---|---|
| Citizen | citizen@demo.local |
Citizen@123 |
| Cleanup Team Leader | tl-od-bbsr-01 |
TeamLead@123 |
| Municipal Officer | mo-od-bbsr-01 |
Officer@123 |
| State Officer | so-odisha-01 |
State@123 |
Citizens may self-register. Official accounts have no public registration. The development reset dialog can display a mock reset token in the demo profile; no real SMS/email delivery is claimed, and no reset token is logged.
To test Gemini Live mode after adding your key, restart the backend and frontend, open the Citizen page, and choose one JPEG or PNG image under 10 MB. A successful live response displays the Gemini Live Analysis badge. A Demo Fallback badge means the key was blank or the live provider was unavailable or invalid; no key is ever sent to the browser.
All Milestone 3 resources use the /api/v1 prefix:
POST /api/v1/auth/register,POST /api/v1/auth/login,GET /api/v1/auth/me, andPOST /api/v1/auth/logoutPOST /api/v1/auth/password-reset/requestandPOST /api/v1/auth/password-reset/confirmPOST /api/v1/reports,GET /api/v1/reports/{reportId}, andGET /api/v1/reports/{reportId}/statusPOST /api/v1/reports/{reportId}/cleanup-feedbackandGET /api/v1/reports/{reportId}/after-evidence/contentPOST /api/v1/locations/google-maps/resolvePOST /api/v1/incidents,GET /api/v1/incidents, andGET /api/v1/incidents/{incidentId}POST /api/v1/incidents/{incidentId}/priority/recalculatePOST /api/v1/incidents/{incidentId}/recipes/generatePOST /api/v1/incidents/{incidentId}/supervisor-review/approvalPOST /api/v1/incidents/{incidentId}/rejectionPOST /api/v1/incidents/{incidentId}/resource-escalationPOST /api/v1/incidents/{incidentId}/recipes/{recipeId}/authorizePOST /api/v1/incidents/{incidentId}/verificationGET /api/v1/incidents/{incidentId}/submitted-imageandGET /api/v1/incidents/{incidentId}/submitted-image/contentGET /api/v1/tasks,GET /api/v1/tasks/{taskId}, andPOST /api/v1/tasks/{taskId}/startPOST /api/v1/tasks/{taskId}/after-evidence(multipart fieldsimageand optionalcompletionNote)GET /api/v1/tasks/{taskId}/after-evidenceandGET /api/v1/tasks/{taskId}/after-evidence/contentPOST /api/v1/tasks/{taskId}/completionGET /api/v1/teamsPOST /api/v1/perception/analyze(multipart fieldimage)GET /api/v1/state/dashboard
The original health endpoint remains available at GET /api/health.
Citizen report creation now requires the analysisId returned by POST /api/v1/perception/analyze. Its JSON contract is:
{
"analysisId": "opaque-uuid-from-perception",
"latitude": 12.97195,
"longitude": 77.59372,
"capturedAt": "2026-08-23T00:00:00Z",
"observationAge": "TODAY",
"comment": "Waste beside the market drain"
}For a Google Maps shared pin, first resolve the approved URL:
POST /api/v1/locations/google-maps/resolve
{
"url": "https://www.google.com/maps/place/Dropped+Pin/@20.1752434,85.7067439,17z"
}The response contains a short-lived locationResolutionId and, when safely available from the URL, a place label. Submit that opaque ID instead of browser-supplied coordinates:
{
"analysisId": "opaque-uuid-from-perception",
"googleMapsLocationId": "opaque-uuid-from-location-resolution",
"capturedAt": "2026-08-23T00:00:00Z",
"observationAge": "ONE_TO_TWO_DAYS",
"comment": "Waste beside the shared pin"
}The location ID expires after 20 minutes and can be used once. When it is present, the backend ignores any browser-supplied latitude or longitude and derives the report coordinates only from the stored server resolution. GPS and explicitly selected demo locations continue to use the latitude/longitude request form shown above.
Fallback analyses, confidence below 70%, hazardous-material flags, and hazardous/e-waste classifications place the incident in SUPERVISOR_REVIEW_REQUIRED. Recipe generation and authorization return HTTP 409 until an officer submits a name and note to the supervisor-review approval endpoint. Approval moves the incident to READY_FOR_AUTHORIZATION; priority and dispatch remain deterministic Java decisions.
The officer's normal acceptance action is Accept & assign team after a recipe has been generated. An officer may instead reject a review-required or ready report with a mandatory short reason. Rejection is blocked after team assignment or any work transition. The Citizen page always provides Track my report. A receipt links to the same dedicated tracking view, which accepts a report reference and polls the real report-to-incident status every eight seconds. It shows Received, Supervisor review required, Team assigned, Work started, Cleanup submitted for verification, Resolved, or Rejected with the officer reason. Only the last report UUID is retained in local browser storage for convenience; no identity data is stored. The backend status record persists locally across restarts.
Citizen submissions accept only JPEG or PNG images. During perception, the backend decodes the upload, bounds it to 1600 pixels on its longest edge, re-encodes it to remove embedded metadata such as EXIF location, and stores only that sanitized representation in the local H2 demo database. The analysis, report, and incident retain an opaque server-side link to this copy; original upload bytes are not retained. District officers can see the submitted image inline and open it in a new tab from incident details and completed records. An unavailable image is stated honestly instead of presenting fabricated evidence.
Worker completion requires a successful after-cleanup evidence upload. The upload accepts one non-empty JPEG or PNG up to 8 MB, validates and decodes the image, bounds it to 1600 pixels on its longest edge, and re-encodes it before placing it in the local H2 demo database. The completion request must include the returned evidence ID and that evidence must belong to the same task:
{
"afterEvidenceId": "opaque-uuid-from-after-evidence-upload",
"completionNote": "Waste removed and work area left safe"
}The officer UI retrieves the evidence through the authenticated backend before enabling verification. The backend independently rejects verification and closure if valid after-cleanup evidence is absent. Privacy-processed evidence is retained in the local demo database so completed history remains viewable after restart; production would move it to controlled object storage.
After upload, the backend sends the privacy-processed citizen before image and worker after image together for a Gemini comparison: CLEANED, PARTIALLY_CLEANED, NOT_CLEANED, or UNCERTAIN, plus visible cleanup percentage, same-location confidence, assessment confidence and an image-based reason. A live after-photo below the minimum cleanup threshold or with low same-location confidence is rejected before evidence is stored. The Worker page shows those scores, the provider's reason, and a corrective next step in a compact rejection card. Every accepted completion waits for explicit Municipal Officer verification; citizen feedback cannot close it. Java owns every workflow transition.
The four Khordha teams and their vehicles begin available. Authorizing dispatch marks the selected district team and vehicle busy, so the Khordha command-center availability metric changes from 4/4 to 3/4. Worker completion does not release those resources. They return to available only after the officer verifies the evidence and closes the incident.
The officer command center refreshes incidents and teams every 10 seconds and after successful actions. Its Workers & teams action lists only the signed-in officer's district resources, including team name, crew size, vehicle and live availability. The active priority queue contains only records that still need action. Active records use a red status indicator and worker-completed records awaiting officer verification use amber. Officer-verified CLOSED incidents move to the green Completed History section with their references, assigned team, key timestamps, location action, and cleanup-evidence availability. Rejected records also leave the action queue and retain their officer reason. Read-only record and evidence actions open in a new browser tab. Completed incidents and tasks remain in durable local history and are never removed or deleted by the UI.
The worker view similarly keeps ASSIGNED, IN PROGRESS, and COMPLETED-awaiting-verification work in its active task area. A bottom-right Select all checklist checkbox selects every required item and can clear them again; the existing start/completion guards still require the full checklist. Once an officer verifies closure, the task moves to Completed Task History with a green verified indicator and its assigned, started, completion-submitted, and verified timestamps. Worker task state refreshes safely every 10 seconds.
Officer and worker views construct Open location in Google Maps links from backend latitude and longitude using Google's standard search URL. These external links require no Maps API key. The action is unavailable when the backend record does not contain valid coordinates; SwachhLens does not embed or silently simulate a map.
Citizen Use my current location calls browser navigator.geolocation only after a click, requests high accuracy, shows the returned accuracy, and never infers a city from an IP address. It requires browser/device permission and a secure context (localhost is accepted by browsers). If GPS is unavailable, imprecise, or the waste is elsewhere, Select exact waste location in Google Maps opens Google Maps in a new tab and accepts a citizen-pasted shared pin link. SwachhLens resolves that approved link on the backend, keeps the coordinates internal to normal citizen UI, and reuses them for fusion, priority, dispatch, and Officer/Worker Google Maps actions.
The previous Nominatim named-place search and embedded Leaflet/OpenStreetMap picker are not part of this flow. Submitted locations and reports are retained in the durable local workflow. The explicit demo location remains available if GPS or link resolution cannot be used.
The landing page links to four separate sign-in panels. Spring Security validates every bearer token and role server-side. Citizens can read only their own reports; Cleanup Team Leaders can read only their assigned team's tasks; Municipal Officers are limited to their configured district; and the State Officer receives Odisha-wide aggregates. This is prototype access control, not a claim of production identity verification.
The State Officer dashboard contains two Leaflet/OpenStreetMap views with attribution: active garbage hotspots and cleanup performance. It reports all 30 Odisha district service areas; operational points come only from persisted citizen reports, so both maps begin empty. Red indicates unresolved/reopened work, orange indicates assigned/in-progress/awaiting-review work, and green indicates completed performance. Fusion retains real report counts for hotspot intensity.
Cleanup teams are approved, preconfigured resources. Crew members have no individual SwachhLens accounts: the Cleanup Team Leader receives the work order for a 1–6-person team. Java recommends one collector for very small/small routine waste without special risk, and increases crew size deterministically for volume, construction waste, drains and hazards. The Municipal Officer still authorizes assignment.
- The landing page uses an original forest-green layout, four-role Login menu, three reporting steps, labelled demo illustrations, FAQ and a local support-note form. The support form sends nothing; it is not a municipal helpdesk.
- Photo selection now has preview/change/remove before Confirm photo & analyze. JPEG/PNG validation, bounding and EXIF removal occur before any provider call. Gemini receives the sanitized representation only.
- The perception contract retains
primaryWasteType,sizeCategory,confidenceScoreand approximate litre bounds, and addsimageEvidenceDecisionplus visible bin/blockage context. A high-confidenceCLEARLY_UNRELATEDlive image is rejected before report creation;AMBIGUOUS, low-confidence, hazardous and fallback findings are review-gated. AI still cannot select staff. - The Citizen form does not ask citizens to estimate observation age or road/drain blockage. The automatic submission timestamp and server-held image findings drive the current workflow.
- Very small or small routine waste with no blockage or hazard recommends one collector. Medium, large, construction, drain and hazardous cases deterministically increase crew/capability requirements. A bin explicitly assessed as not full can remain a normal-collection recommendation with no special dispatch.
- Same-district active incidents accept matching reports within 100 m beyond 45 minutes without creating another task. Review pauses preserve existing assignments and recipes. Citizen reopening blocks rework until explicit supervisor approval of the retained assignment; a new after-photo is required for the new attempt.
- Teams have fixed district/garage coordinates, capabilities, crew and vehicles. P1 ranks nearest eligible team first. P2–P4 rank lowest daily assigned workload, then Haversine distance and stable ID. Assignment points are P1=100, P2=80, P3=60, P4=40, counted on assignment date in Asia/Kolkata. Midnight never erases or reassigns an unfinished task.
- Authorization rechecks district, capacity, capability, team/vehicle availability and duplicate task inside a shared in-process reservation section. No eligible team produces a visible
dispatchBlockReason; it never invents an assignment. The officer may record a resource-escalation request while the incident remains in the active queue. This is single-JVM atomicity, not distributed coordination. GET /api/v1/teamsincludestodayWorkloadScore; availability reflects current team and vehicle assignment state.- All 30 official Odisha district names are configured as service areas. The prototype rejects coordinates clearly outside an Odisha-only envelope, then routes in-state coordinates to the nearest configured district headquarters. This is a service-centre approximation, not authoritative point-in-polygon administration: the official NIC boundary service timed out during this upgrade, so border-adjacent reports require officer confirmation before production use. State hotspot intensity uses persisted report/status/priority data only.
- All preconfigured teams have leader accounts. Existing Bhubaneswar logins stay valid. District officers use
mo-od-<district-lowercase>-01/Officer@123; team leaders use their seededtl-od-...code /TeamLead@123. These credentials are synthetic, local-only, and must never be deployed publicly.
Use consented, non-identifying JPEG/PNG fixtures. Record provider source, flags, confidence, review gate and Java recommendation. This matrix is for manual validation after a real key is configured; it has not been scored as an accuracy benchmark.
| Fixture | Expected safe behavior |
|---|---|
| Clean area / tiny litter | No special dispatch; normal collection guidance |
| Normal bin, not full | Normal collection route, no special team |
| Small overflowing bin, no blockage | Two-person routine bin-service unit |
| Small roadside pile | Two-person minimum, capable available vehicle |
| Medium dump | Three-person minimum and sufficient vehicle capacity |
| Large dump / drain blocked | At least four approved crew members |
| Construction debris | Five-person recommendation plus construction capability |
| Hazardous / e-waste | Specialist capability/vehicle and mandatory review |
| Blurry, dark, unrelated or unclear scale | Low confidence/uncertainty review or explicit fallback |
| Citizen blockage contradicts image | Review required; no browser override of AI findings |
No custom model training, perfect accuracy, road travel-time routing, real SMS/email, recruitment, payroll or government deployment is claimed.
Open a second PowerShell window in the repository root:
cd frontend
npm install
npm run devOpen http://localhost:5173. During development, Vite proxies /api requests to http://localhost:8080.
Latest verification (9 September 2026): 88 backend tests passed, with zero failures, errors or skipped tests; Maven verify, frontend lint and production build passed. The frontend build has a non-blocking 538 kB JavaScript chunk warning.
Local fallback-mode API verification covered image analysis, report fusion, the supervisor gate (HTTP 409 before approval), assignment, worker start, actual PNG evidence upload, completion, officer closure, citizen Resolved status and team release. Browser checks covered all four role logins, district scope, state map toggles, citizen tracking, desktop and 375px layouts without horizontal overflow. Native file-picker automation timed out, so browser photo-selection/preview end-to-end verification remains a manual follow-up; the multipart image endpoints were exercised separately. No live Gemini call or AI accuracy benchmark was performed during these checks.
Backend test suite:
cd backend
$env:JAVA_HOME = 'C:\Program Files\Java\jdk-17'
.\mvnw.cmd testBackend full build:
cd backend
$env:JAVA_HOME = 'C:\Program Files\Java\jdk-17'
.\mvnw.cmd verifyFrontend lint and production build:
cd frontend
npm install
npm run lint
npm run buildbackend/.env.examplecontains only the Gemini key and model placeholders required for this milestone.frontend/.env.exampledocumentsVITE_API_BASE_URL.
Do not commit real .env files. This project explicitly imports the optional backend/.env file when the backend is started from the backend directory. Operating-system environment variables can also provide the same configuration.
For local frontend development, leave VITE_API_BASE_URL unset so Vite uses its /api proxy. In a deployed frontend, set it to the public backend origin.
The database Spring profile is configured to read DB_URL, DB_USERNAME, and DB_PASSWORD, validate the Hibernate schema, and run Flyway migrations. It is intentionally not exercised until database entities and migrations are approved for a later milestone.
SwachhLens/
├── backend/ # Spring Boot REST API
├── frontend/ # React/Vite web application
├── AGENTS.md # repository implementation conventions
├── PLAN.md # approved delivery plan
└── README.md