CityDetail table to reduce API calls and keep user records linked to a canonical place.
Overview
- Primary: OpenCage Geocoding API — reverse geocode (lat, lon) to get timezone, city, state, country, and optional address/confidence.
- Fallback: Longitude-based timezone estimation and simple location labels (no external API), in
helpers/simple_timezone_fallback.py. - Caching:
get_city_hybrid(and thusget_or_create_city) returns or creates aCityDetailrow so the same (city, state, country) is reused and the user’scity_idpoints to it.
OPENCAGE_API_KEY. Without it, the primary path raises when the geocoder is first used; the fallback is only used after an OpenCage call fails (e.g. network error or empty result).
Main functions
get_timezone_hybrid(lat, lon)
File: helpers/hybrid_geocoding.py
Returns the IANA timezone name (e.g. America/New_York, Asia/Kolkata) for the given coordinates.
- OpenCage — Call
reverse_geocode(lat, lon), readannotations.timezone.namefrom the first result. If present, return it and log. - Fallback — If OpenCage fails or returns no timezone, call
get_timezone_from_longitude(lat, lon)insimple_timezone_fallback.py, which maps longitude ranges to a fixed set of timezone names. If that fails, return"UTC".
get_city_hybrid is used.
get_location_info_hybrid(lat, lon)
File: helpers/hybrid_geocoding.py
Returns a dictionary with city, state, country, timezone, and metadata (no database write).
- OpenCage — Reverse geocode and parse
components(city/town/village/municipality/suburb/county/state) andannotations.timezone. City is resolved with a long fallback chain (city → town → village → … → county → state →"Unknown Location"). Result includessource: 'opencage', confidence, formatted address. - Fallback — If OpenCage fails, call
get_simple_location_info(lat, lon)insimple_timezone_fallback.py, which usesget_timezone_from_longitudeand coarse lat/lon rules (e.g. North America, India) to produce a minimal dict with city/state/country/timezone.
get_city_hybrid; not typically called directly from the main app.
get_city_hybrid(lat, lon)
File: helpers/hybrid_geocoding.py
Returns a CityDetail instance (existing or newly created) for the given coordinates.
- Call
get_location_info_hybrid(lat, lon)to get city, state, country, timezone. - Look up
CityDetailby(city, state, country). If found, optionally update itstimezoneif it was missing or different, then return it. - If not found, create a new
CityDetailwith city, state, country, timezone, latitude, longitude; commit and return.
CityDetail to attach to a user (e.g. user.city_id) and for display names in panchangam.
get_or_create_city(lat, lon)
File: helpers/city_utils.py
Thin wrapper that delegates to get_city_hybrid(lat, lon). Exists for backward compatibility and clearer naming at call sites. Same behavior: returns a CityDetail or None if geocoding fails.
get_timezone_from_opencage(lat, lon)
File: helpers/city_utils.py
Alias for get_timezone_hybrid (imported from hybrid_geocoding). Used by tests and any code that expects a “from OpenCage” name; behavior is the same hybrid (OpenCage first, then longitude fallback).
Longitude-based fallback
File:helpers/simple_timezone_fallback.py
get_timezone_from_longitude(lat, lon)— Splits the globe into longitude bands and returns a fixed IANA timezone per band (e.g. -90 to -67.5 →America/New_York, 60–75 →Asia/Kolkata). Latitude is not used. Intended as a last resort when APIs fail.get_simple_location_info(lat, lon)— Returns a dict withcity,state,country,timezoneusing the same timezone logic plus coarse regional rules (e.g. North America, India) to set generic labels. Used byget_location_info_hybridwhen OpenCage fails.
Caching in CityDetail
TheCityDetail model (see Database) stores:
city,state,country— Used as the unique logical key together (lookup inget_city_hybrid).timezone— Can be updated when an existing row is found but had no timezone or a different one.latitude,longitude— Stored on create; not used for lookup.
get_city_hybrid (or get_or_create_city) whenever the app needs a place for coordinates:
- Repeated coordinates (or same city/state/country from OpenCage) reuse one row.
- UserDetail rows reference it via
city_id, so panchangam and admin can show a single canonical name and timezone per user.
Where it’s used in the codebase
app.py— Location webhook:get_timezone_hybrid(lat, lon)for initial timezone;get_city_hybrid(lat, lon)forCityDetail, then user’stimezoneis set from the city’s timezone if available, anduser.city_idis set. Test route (e.g. generate-by-coords):get_city_hybridfor optional city display.admin_portal/app.py— When an admin updates a user’s location (new lat/lon),get_timezone_hybridandget_city_hybridare used to resolve timezone and city and update the user record.- Tests — e.g.
test_app_integration.pyusesget_timezone_from_opencageandget_or_create_cityto verify geocoding and DB caching.
Summary
| Goal | Function | Primary source | Fallback |
|---|---|---|---|
| Timezone only | get_timezone_hybrid | OpenCage annotations.timezone.name | get_timezone_from_longitude → else UTC |
| Location dict (no DB) | get_location_info_hybrid | OpenCage components + timezone | get_simple_location_info |
| City row (cached) | get_city_hybrid / get_or_create_city | Above + CityDetail lookup/insert | Same as above; if both fail, returns None |
helpers/ (hybrid_geocoding.py, city_utils.py, simple_timezone_fallback.py). The opencage Python package is used to call the OpenCage API; the key is configured via OPENCAGE_API_KEY.