-
Notifications
You must be signed in to change notification settings - Fork 1
Geocoding Cron Job (#76) #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tqmsh
wants to merge
4
commits into
main
Choose a base branch
from
TQ/geocoding-refresh-cron-job
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
38f78bb
Geocoding Cron Job (#76)
Yujie-Meng c8e9085
Refactor geocoding refresh job to use GoogleMapsClient
tqmsh 8ee1b50
Merge branch 'main' of https://github.com/uwblueprint/food4kids into …
ludavidca 121ecba
Temp commit, currently broken
ludavidca File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
backend/python/app/services/jobs/geocoding_refresh_jobs.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| """Geocoding refresh scheduled jobs""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from datetime import datetime, timedelta | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| if TYPE_CHECKING: | ||
| from uuid import UUID | ||
|
|
||
| from sqlalchemy import and_, or_ | ||
github-code-quality[bot] marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||
| from sqlmodel import select | ||
|
|
||
| from app.dependencies.services import get_logger | ||
| from app.models import async_session_maker_instance | ||
| from app.models.admin import Admin | ||
| from app.models.location import Location | ||
| from app.utilities.geocoding import geocode_addresses | ||
|
||
|
|
||
|
|
||
| async def _get_archive_threshold(session) -> int: | ||
| """Get the route archive threshold from admin settings""" | ||
| logger = get_logger() | ||
| statement = select(Admin) | ||
| result = await session.execute(statement) | ||
| admin_record = result.scalars().first() | ||
|
|
||
| if not admin_record: | ||
| logger.warning("No admin record found, using default threshold of 30 days") | ||
| return 30 | ||
|
|
||
| threshold = admin_record.route_archive_after or 30 | ||
| logger.info(f"Using route_archive_after threshold: {threshold} days") | ||
| return threshold | ||
|
|
||
|
|
||
| async def _get_locations_to_refresh(session, cutoff_date) -> list[Location]: | ||
| """Query locations that need geocoding refresh""" | ||
| logger = get_logger() | ||
| statement = select(Location).where( | ||
| or_( | ||
| Location.geocoded_at.is_(None), | ||
| Location.geocoded_at < cutoff_date | ||
| ) | ||
| ) | ||
| result = await session.execute(statement) | ||
| locations = result.scalars().all() | ||
| logger.info(f"Found {len(locations)} locations to refresh") | ||
| return locations | ||
|
|
||
|
|
||
| async def _refresh_locations(session, locations: list[Location]) -> int: | ||
| """Batch geocode and update locations, returns count of successfully refreshed""" | ||
| logger = get_logger() | ||
|
|
||
| if not locations: | ||
| logger.info("No locations need geocoding refresh") | ||
| return 0 | ||
|
|
||
| addresses = [loc.address for loc in locations] | ||
| geocoding_results = await geocode_addresses(addresses) | ||
|
|
||
| refreshed_count = 0 | ||
| for location, coords in zip(locations, geocoding_results): | ||
| if coords: | ||
| location.latitude = coords["lat"] | ||
| location.longitude = coords["lng"] | ||
| location.geocoded_at = datetime.now() | ||
| refreshed_count += 1 | ||
| logger.info( | ||
| f"Refreshed geocoding for location {location.location_id} " | ||
| f"({location.address}): {coords}" | ||
| ) | ||
| else: | ||
| logger.warning( | ||
| f"Failed to geocode location {location.location_id} " | ||
| f"({location.address})" | ||
| ) | ||
|
|
||
| await session.commit() | ||
| logger.info(f"Successfully refreshed {refreshed_count}/{len(locations)} locations") | ||
| return refreshed_count | ||
|
|
||
|
|
||
| async def refresh_geocoding() -> None: | ||
| """Refresh geocoding for locations - runs daily | ||
|
|
||
| This job: | ||
| 1. Gets the route_archive_after setting from admin_info (default 30 days) | ||
| 2. Finds all locations that need refreshing (geocoded_at is null or older than threshold) | ||
| 3. Refreshes lat/lon for those locations using Google Geocoding API | ||
| 4. Updates geocoded_at timestamp for refreshed locations | ||
| """ | ||
| logger = get_logger() | ||
|
|
||
| if async_session_maker_instance is None: | ||
| logger.error("Database session maker not initialized") | ||
| return | ||
|
|
||
| try: | ||
| async with async_session_maker_instance() as session: | ||
| admin_statement = select(Admin) | ||
| admin_result = await session.execute(admin_statement) | ||
| admin_record = admin_result.scalars().first() | ||
github-code-quality[bot] marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||
|
|
||
| archive_threshold_days = await _get_archive_threshold(session) | ||
| cutoff_date = datetime.now() - timedelta(days=archive_threshold_days) | ||
|
|
||
| locations = await _get_locations_to_refresh(session, cutoff_date) | ||
| await _refresh_locations(session, locations) | ||
|
|
||
| except Exception as error: | ||
| logger.error( | ||
| f"Failed to refresh geocoding: {error!s}", exc_info=True | ||
| ) | ||
| raise error | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.