Auto-update: Wed Jun 26 12:53:04 PDT 2024

This commit is contained in:
sanj 2024-06-26 12:53:04 -07:00
parent c5a4242ec4
commit ab86abc2f2
3 changed files with 64 additions and 34 deletions

View file

@ -109,8 +109,6 @@ class IncomingEmail(BaseModel):
class Location(BaseModel): class Location(BaseModel):
latitude: float latitude: float
longitude: float longitude: float

View file

@ -29,7 +29,7 @@ from sijapi.utilities import haversine
locate = APIRouter() locate = APIRouter()
async def reverse_geocode(latitude: float, longitude: float) -> Optional[Location]: async def reverse_geocode(latitude: float, longitude: float, elevation: float = None) -> Optional[Location]:
url = f"https://nominatim.openstreetmap.org/reverse?format=json&lat={latitude}&lon={longitude}" url = f"https://nominatim.openstreetmap.org/reverse?format=json&lat={latitude}&lon={longitude}"
L.INFO(f"Calling Nominatim API at {url}") L.INFO(f"Calling Nominatim API at {url}")
headers = { headers = {
@ -42,21 +42,22 @@ async def reverse_geocode(latitude: float, longitude: float) -> Optional[Locatio
data = await response.json() data = await response.json()
address = data.get("address", {}) address = data.get("address", {})
elevation = elevation or await get_elevation(latitude, longitude)
location = Location( location = Location(
latitude=float(data.get("lat", latitude)), latitude=latitude,
longitude=float(data.get("lon", longitude)), longitude=longitude,
elevation=elevation,
datetime=datetime.now(timezone.utc), datetime=datetime.now(timezone.utc),
zip=address.get("postcode"), zip=address.get("postcode"),
street=address.get("road"), street=address.get("road"),
city=address.get("city"), city=address.get("city"),
state=address.get("state"), state=address.get("state"),
country=address.get("country"), country=address.get("country"),
context={}, # Initialize with an empty dict, to be filled as needed context={},
class_=data.get("class"), class_=data.get("class"),
type=data.get("type"), type=data.get("type"),
name=data.get("name"), name=data.get("name"),
display_name=data.get("display_name"), display_name=data.get("display_name"),
boundingbox=data.get("boundingbox"),
amenity=address.get("amenity"), amenity=address.get("amenity"),
house_number=address.get("house_number"), house_number=address.get("house_number"),
road=address.get("road"), road=address.get("road"),
@ -197,7 +198,8 @@ def find_override_locations(lat: float, lon: float) -> Optional[str]:
return closest_location return closest_location
def get_elevation(latitude, longitude):
async def get_elevation(latitude, longitude):
url = "https://api.open-elevation.com/api/v1/lookup" url = "https://api.open-elevation.com/api/v1/lookup"
payload = { payload = {
@ -209,11 +211,12 @@ def get_elevation(latitude, longitude):
] ]
} }
async with aiohttp.ClientSession() as session:
try: try:
response = requests.post(url, json=payload) async with session.post(url, json=payload) as response:
response.raise_for_status() # Raise an exception for unsuccessful requests response.raise_for_status() # Raise an exception for unsuccessful requests
data = response.json() data = await response.json()
if "results" in data: if "results" in data:
elevation = data["results"][0]["elevation"] elevation = data["results"][0]["elevation"]
@ -221,12 +224,13 @@ def get_elevation(latitude, longitude):
else: else:
return None return None
except requests.exceptions.RequestException as e: except aiohttp.ClientError as e:
L.ERR(f"Error: {e}") L.ERR(f"Error: {e}")
return None return None
async def fetch_locations(start: datetime, end: datetime = None) -> List[Location]: async def fetch_locations(start: datetime, end: datetime = None) -> List[Location]:
start_datetime = await localize_datetime(start) start_datetime = await localize_datetime(start)
if end is None: if end is None:
@ -385,7 +389,6 @@ async def generate_map(start_date: datetime, end_date: datetime):
return html_content return html_content
async def post_location(location: Location): async def post_location(location: Location):
L.DEBUG(f"post_location called with {location.datetime}") L.DEBUG(f"post_location called with {location.datetime}")
@ -401,26 +404,50 @@ async def post_location(location: Location):
# Parse and localize the datetime # Parse and localize the datetime
localized_datetime = await localize_datetime(location.datetime) localized_datetime = await localize_datetime(location.datetime)
await conn.execute(''' await conn.execute('''
INSERT INTO locations (datetime, location, city, state, zip, street, action, device_type, device_model, device_name, device_os) INSERT INTO locations (
VALUES ($1, ST_SetSRID(ST_MakePoint($2, $3, $4), 4326), $5, $6, $7, $8, $9, $10, $11, $12, $13) datetime, location, city, state, zip, street, action, device_type, device_model, device_name, device_os,
''', localized_datetime, location.longitude, location.latitude, location.elevation, location.city, location.state, location.zip, location.street, action, device_type, device_model, device_name, device_os) class_, type, name, display_name, amenity, house_number, road, quarter, neighbourhood,
suburb, county, country_code, country
)
VALUES ($1, ST_SetSRID(ST_MakePoint($2, $3, $4), 4326), $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15,
$16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)
''', localized_datetime, location.longitude, location.latitude, location.elevation, location.city, location.state,
location.zip, location.street, action, device_type, device_model, device_name, device_os,
location.class_, location.type, location.name, location.display_name,
location.amenity, location.house_number, location.road, location.quarter, location.neighbourhood,
location.suburb, location.county, location.country_code, location.country)
await conn.close() await conn.close()
L.INFO(f"Successfully posted location: {location.latitude}, {location.longitude} on {localized_datetime}") L.INFO(f"Successfully posted location: {location.latitude}, {location.longitude}, {location.elevation} on {localized_datetime}")
return { return {
'datetime': localized_datetime, 'datetime': localized_datetime,
'latitude': location.latitude, 'latitude': location.latitude,
'longitude': location.longitude, 'longitude': location.longitude,
'elevation': location.elevation,
'city': location.city, 'city': location.city,
'state': location.state, 'state': location.state,
'zip': location.zip, 'zip': location.zip,
'street': location.street, 'street': location.street,
'elevation': location.elevation,
'action': action, 'action': action,
'device_type': device_type, 'device_type': device_type,
'device_model': device_model, 'device_model': device_model,
'device_name': device_name, 'device_name': device_name,
'device_os': device_os 'device_os': device_os,
'class_': location.class_,
'type': location.type,
'name': location.name,
'display_name': location.display_name,
'amenity': location.amenity,
'house_number': location.house_number,
'road': location.road,
'quarter': location.quarter,
'neighbourhood': location.neighbourhood,
'suburb': location.suburb,
'county': location.county,
'country_code': location.country_code,
'country': location.country
} }
except Exception as e: except Exception as e:
L.ERR(f"Error posting location {e}") L.ERR(f"Error posting location {e}")
@ -428,6 +455,7 @@ async def post_location(location: Location):
return None return None
@locate.post("/locate") @locate.post("/locate")
async def post_locate_endpoint(locations: Union[Location, List[Location]]): async def post_locate_endpoint(locations: Union[Location, List[Location]]):
responses = [] responses = []
@ -451,22 +479,26 @@ async def post_locate_endpoint(locations: Union[Location, List[Location]]):
"device_os": "Unknown" "device_os": "Unknown"
} }
L.DEBUG(f"datetime before localization: {location.datetime}") # L.DEBUG(f"datetime before localization: {location.datetime}")
# Convert datetime string to timezone-aware datetime object
location.datetime = await localize_datetime(location.datetime) location.datetime = await localize_datetime(location.datetime)
L.DEBUG(f"datetime after localization: {location.datetime}") # L.DEBUG(f"datetime after localization: {location.datetime}")
L.DEBUG(f"Location received for processing: {location}")
# Perform reverse geocoding # Perform reverse geocoding
geocoded_location = await reverse_geocode(location.latitude, location.longitude) geocoded_location = await reverse_geocode(location.latitude, location.longitude)
if geocoded_location: if geocoded_location:
# Update location with geocoded information L.DEBUG(f"Reverse geocoding result: {geocoded_location}")
for field in location.__fields__: for field in location.__fields__:
if getattr(location, field) is None: if getattr(location, field) is None:
setattr(location, field, getattr(geocoded_location, field)) setattr(location, field, getattr(geocoded_location, field))
else:
L.WARN(f"Geocoding failed!")
L.DEBUG(f"Final location submitted to database: {location}")
location_entry = await post_location(location) location_entry = await post_location(location)
if location_entry: if location_entry:
responses.append({"location_data": location_entry}) # Add weather data if necessary responses.append({"location_data": location_entry}) # Add weather data if necessary
else:
L.WARN(f"Posing location to database appears to have failed.")
return {"message": "Locations and weather updated", "results": responses} return {"message": "Locations and weather updated", "results": responses}

View file

@ -667,7 +667,7 @@ async def generate_banner(dt, location: Location = None, forecast: str = None, m
if location.display_name: if location.display_name:
display_name += f"{location.display_name}" display_name += f"{location.display_name}"
else: elif:
display_name += f"{location.road}, " if location.road else "" display_name += f"{location.road}, " if location.road else ""
display_name += f"the {location.neighbourhood} neighbourhood of " if location.neighbourhood else "" display_name += f"the {location.neighbourhood} neighbourhood of " if location.neighbourhood else ""
display_name += f"the {location.suburb} suburb of " if location.suburb else "" display_name += f"the {location.suburb} suburb of " if location.suburb else ""