Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

FuelUp

FuelUp is a production-oriented route and fuel planning platform built with Django 6 and Next.js 16. It accepts a start and finish location inside the United States and returns:

  • The best evaluated road-route alternative as GeoJSON.
  • A highlighted interactive map with numbered fuel stops.
  • A fuel purchase plan that respects a 500-mile vehicle range.
  • Total gallons and estimated fuel cost at 10 MPG.
  • Operational metadata for caching, route selection, and providers.

The backend uses the supplied fuel-prices.csv, GeoNames-derived station coordinates, Nominatim geocoding, and OSRM routing. The frontend uses TypeScript, React Leaflet, and a server-side Next.js proxy.

Production features

  • One OSRM request asks for multiple route alternatives.
  • Every alternative is evaluated locally against fuel prices.
  • Fuel purchasing is cost-optimal for a fixed route under the configured tank and MPG assumptions.
  • Stations are projected onto indexed route segments instead of matched only to route vertices.
  • Complete route responses and geocodes are cached.
  • Twenty-five bundled frontend demo routes keep the product explorable while the free backend is sleeping or unavailable.
  • Redis-backed request rate limiting works across application instances.
  • Cache stampede protection prevents duplicate provider work.
  • Request IDs, structured JSON logs, liveness, and readiness probes are built in.
  • Django and Next.js run as non-root, health-checked containers.
  • Render and Vercel deployment manifests are committed.
  • CI enforces linting, branch coverage, performance, dependency audit, deployment-manifest validation, and container builds.

Architecture

Browser
|
v
Next.js frontend
| same-origin /api/route proxy
v
Django API
|
+--> Redis: route cache, geocode cache, rate-limit counters
+--> Nominatim: start and finish geocoding
+--> OSRM: one request containing route alternatives
+--> Local station dataset and optimizer

Backend responsibilities are separated by layer:

routes/
├── api/ HTTP validation, rate limiting, request context
├── application/ Route-plan orchestration and response caching
├── domain/ Entities, route geometry, fuel optimization
├── infrastructure/ Map providers and station repository
├── management/ Reproducible station-data preparation
└── tests/ Unit, integration, performance, and contract tests

The domain layer does not depend on Django HTTP code or provider clients. This keeps optimization and geometry independently testable.

Quick start

Docker

Docker is the closest local match to production:

docker compose up --build

If your Docker installation uses the legacy standalone command:

docker-compose up --build

Open http://localhost:3000. Django is exposed at http://localhost:8000.

The stack includes:

  • Next.js frontend
  • Django/Gunicorn backend
  • Redis cache and rate-limit store

Native development

Requirements:

  • Python 3.12+
  • Node.js 20.9+

Backend:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
python manage.py check
python manage.py runserver

Frontend, in another terminal:

cd frontend
cp .env.example .env.local
npm ci
npm run dev

Open http://localhost:3000.

API usage

Health:

curl http://127.0.0.1:8000/api/health/live/
curl http://127.0.0.1:8000/api/health/ready/

Route plan:

curl -X POST http://127.0.0.1:8000/api/route/ \
-H "Content-Type: application/json" \
-H "X-Request-ID: local-test-1" \
-d '{"start":"Los Angeles, CA","finish":"New York, NY"}'

Important response headers:

HeaderMeaning
X-Request-IDCorrelation ID included in server logs.
X-FuelUp-CacheHIT or MISS for the complete route response.
X-FuelUp-Cache-TTLConfigured maximum route cache lifetime in seconds.
X-RateLimit-LimitRequests allowed in the configured window.
X-RateLimit-RemainingRequests remaining for the client.
Retry-AfterSeconds to wait after a 429 response.

The full contract is in openapi.yaml.

Bundled demo routes

The frontend includes 25 representative U.S. route snapshots in frontend/lib/demo-routes.ts. The folded demo widget renders these through the same summary, map, and fuel-stop components without calling Django. They are explicitly labeled as illustrative frontend data and are not presented as live OSRM or optimizer results.

Route and fuel optimization

An uncached request makes at most three external calls:

  1. Geocode the start.
  2. Geocode the finish.
  3. Ask OSRM for route alternatives in one routing call.

For each OSRM alternative, FuelUp:

  1. Builds a spatial index of route segments.
  2. Projects nearby station coordinates onto the closest segment.
  3. Orders stations by accurate route-mile progress.
  4. Validates that no coverage gap exceeds 500 miles.
  5. Computes the minimum fuel-purchase cost for that fixed route.
  6. Scores the route using fuel cost plus configurable time and stop penalties.

The fixed-route fuel policy is:

  • If a cheaper station is reachable, buy only enough to reach the first one.
  • If no cheaper station is reachable, carry as much cheaper fuel forward as the 50-gallon tank allows.
  • Equal-price stations are consolidated so they do not create needless top-offs.

This policy is cost-optimal for an ordered fixed route with deterministic prices, constant MPG, and no per-stop fixed charge. The route-level score then adds operational preferences without altering the reported fuel cost.

The detailed comparison with brute force, naive greedy, the previous dynamic program, and the selected approach is in understanding.md.

Station data

The supplied CSV has station prices and city/state fields but no coordinates. The generated data/fuel-stations.csv enriches U.S. rows with approximate GeoNames postal-locality coordinates.

Regenerate it with:

python manage.py build_station_data

The generated data is committed so route requests never geocode thousands of stations. Attribution and accuracy details are in data/README.md.

Configuration

Copy .env.example and set production values through the hosting platform.

VariablePurpose
DJANGO_SECRET_KEYRequired secret in production.
DJANGO_ALLOWED_HOSTSComma-separated API host names.
DATABASE_URLSQLite locally or PostgreSQL URL when needed.
REDIS_URLShared cache and rate-limit backend.
EXTERNAL_API_USER_AGENTReal app/contact identity for Nominatim.
GEOCODE_CACHE_SECONDSGeocode cache TTL.
ROUTE_CACHE_SECONDSFull route-plan cache TTL (default: 30 days).
ROUTE_CACHE_LOCK_WAIT_SECONDSWait for an identical in-flight route before recomputing.
WARM_COMMON_ROUTESWarm the three frontend preset routes before startup.
ROUTE_ALTERNATIVESAlternatives requested in the single OSRM call.
ROUTE_GEOMETRY_OVERVIEWOSRM geometry detail; simplified is optimized for API latency.
ROUTE_TIME_VALUE_USD_PER_HOURRoute-selection time weighting.
ROUTE_STOP_PENALTY_USDRoute-selection stop weighting.
ROUTE_RATE_LIMIT_REQUESTSRequests per client per window.
ROUTE_RATE_LIMIT_WINDOW_SECONDSRate-limit window length.

Production validation:

DJANGO_SETTINGS_MODULE=fuelup.settings.production \
DJANGO_SECRET_KEY='a-long-random-secret' \
python manage.py check --deploy

Quality checks

Backend:

ruff check fuelup routes scripts manage.py gunicorn.conf.py
coverage run manage.py test
coverage report
python manage.py check
python scripts/validate_manifests.py

Frontend:

cd frontend
npm run lint
npm run build
npm audit --omit=dev --audit-level=high

Containers:

docker build -t fuelup-backend .
docker build -t fuelup-frontend frontend

The current suite contains 37 tests and enforces at least 80% backend branch coverage. A performance regression test requires a 1,000-station optimization case to complete in under 500 ms.

Deployment

The next deployment target is already prepared:

Follow docs/deployment.md for environment variables, health checks, verification, and rollback.

Route plans are cached for 30 days by default. Render's free Key Value service does not provide disk persistence, so entries can still disappear after a cache-service restart or eviction. Check X-FuelUp-Cache (HIT or MISS) and X-FuelUp-Cache-TTL when diagnosing a deployed request.

Known limitations

  • Station coordinates are city/postal approximations because the source CSV does not provide exact latitude/longitude.
  • The 25-mile corridor is a candidate filter. Exact station driveway detours are not separately routed because doing so would violate the project's one-to-three external-call target.
  • Fuel prices are static input data, not live prices.
  • Public Nominatim and OSRM endpoints do not provide a production SLA. A commercial deployment should use self-hosted or contracted compatible providers through the configurable base URLs.
  • The optimizer assumes constant 10 MPG and does not model elevation, traffic, weather, vehicle-specific restrictions, or price changes during the trip.

About

Helps truckers to get the optimal pocket-friendly path between 2 locations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages