diff --git a/work_trace/models/wt_location_log.py b/work_trace/models/wt_location_log.py index d3a68247..5cafea70 100644 --- a/work_trace/models/wt_location_log.py +++ b/work_trace/models/wt_location_log.py @@ -2,7 +2,6 @@ from odoo import models, fields, api, _ from math import radians, sin, cos, sqrt, atan2 -# Maps Google/OSM activity types to human-readable categories CATEGORY_MAP = { 'IN_PASSENGER_VEHICLE': 'In Vehicle', 'IN_VEHICLE': 'In Vehicle', @@ -25,11 +24,7 @@ class WtLocationLog(models.Model): _description = 'WorkTrace Location Log' _order = 'arrived_at asc' - name = fields.Char( - string='Name', - compute='_compute_name', - store=True - ) + name = fields.Char(string='Name', compute='_compute_name', store=True) date = fields.Date(string='Date', required=True, index=True) arrived_at = fields.Datetime(string='Begin Time', required=True) departed_at = fields.Datetime(string='End Time') @@ -38,8 +33,6 @@ class WtLocationLog(models.Model): longitude = fields.Float(string='Longitude', digits=(10, 7)) address = fields.Char(string='Address') place_name = fields.Char(string='Place / Business Name') - - # Category: OSM-derived for stops, activity-type for travel segments category = fields.Char(string='Category') time_at_location = fields.Float( @@ -47,19 +40,14 @@ class WtLocationLog(models.Model): compute='_compute_time_at_location', store=True ) - distance_from_previous = fields.Float( - string='Distance (mi)', - digits=(10, 2) - ) - travel_time_from_previous = fields.Float( - string='Travel Time (hrs)', - digits=(10, 2) - ) + distance_from_previous = fields.Float(string='Distance (mi)', digits=(10, 2)) + travel_time_from_previous = fields.Float(string='Travel Time (hrs)', digits=(10, 2)) travel_mode = fields.Selection([ ('driving', 'Driving'), ('walking', 'Walking'), - ('transit', 'Transit'), ('cycling', 'Cycling'), + ('transit', 'Transit'), + ('flying', 'Flying'), ('unknown', 'Unknown'), ], string='Travel Mode', default='unknown') @@ -68,11 +56,7 @@ class WtLocationLog(models.Model): ('manual', 'Manual'), ], string='Source', default='google_timeline') - calendar_event_id = fields.Many2one( - 'calendar.event', - string='Calendar Event', - ondelete='set null' - ) + calendar_event_id = fields.Many2one('calendar.event', string='Calendar Event', ondelete='set null') add_to_calendar = fields.Boolean(string='Add to Calendar', default=True) notes = fields.Text(string='Notes') @@ -89,35 +73,27 @@ class WtLocationLog(models.Model): def _compute_time_at_location(self): for rec in self: if rec.arrived_at and rec.departed_at: - delta = rec.departed_at - rec.arrived_at - rec.time_at_location = delta.total_seconds() / 3600 + rec.time_at_location = (rec.departed_at - rec.arrived_at).total_seconds() / 3600 else: rec.time_at_location = 0.0 @staticmethod def _haversine_distance(lat1, lon1, lat2, lon2): - """Calculate distance in miles between two GPS coordinates.""" R = 3958.8 lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) - dlat = lat2 - lat1 - dlon = lon2 - lon1 + dlat, dlon = lat2 - lat1, lon2 - lon1 a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2 return R * 2 * atan2(sqrt(a), sqrt(1 - a)) def action_geocode(self): """Resolve coordinates to address using OpenStreetMap Nominatim. - - Processes up to GEOCODE_BATCH_SIZE records per call to avoid HTTP - worker timeouts (Nominatim enforces a 1 req/sec rate limit). - Call repeatedly until the notification reports 0 remaining. + Processes up to GEOCODE_BATCH_SIZE records per call to avoid HTTP worker timeouts. """ import requests import time - # From the selected/passed recordset, take only those needing geocoding to_geocode = self.filtered(lambda r: r.latitude and r.longitude and not r.address) if not to_geocode: - # If all selected already have addresses, allow re-geocoding but still batch to_geocode = self.filtered(lambda r: r.latitude and r.longitude) batch = to_geocode[:GEOCODE_BATCH_SIZE] @@ -125,12 +101,7 @@ class WtLocationLog(models.Model): for rec in batch: url = 'https://nominatim.openstreetmap.org/reverse' - params = { - 'lat': rec.latitude, - 'lon': rec.longitude, - 'format': 'json', - 'addressdetails': 1, - } + params = {'lat': rec.latitude, 'lon': rec.longitude, 'format': 'json', 'addressdetails': 1} headers = {'User-Agent': 'WorkTrace/1.0 (Odoo Module)'} try: resp = requests.get(url, params=params, headers=headers, timeout=10) @@ -148,16 +119,10 @@ class WtLocationLog(models.Model): or addr.get('tourism') or '' ) - osm_type = ( - addr.get('amenity') - or addr.get('shop') - or addr.get('tourism') - or addr.get('leisure') - or addr.get('office') - or addr.get('building') - or result.get('type', '') - or result.get('class', '') + addr.get('amenity') or addr.get('shop') or addr.get('tourism') + or addr.get('leisure') or addr.get('office') or addr.get('building') + or result.get('type', '') or result.get('class', '') ) if osm_type and not rec.category: rec.category = osm_type.replace('_', ' ').title() @@ -167,11 +132,7 @@ class WtLocationLog(models.Model): except Exception: pass - # Count all location logs still missing an address - remaining = self.search_count([ - ('latitude', '!=', 0), - ('address', '=', False), - ]) + remaining = self.search_count([('latitude', '!=', 0), ('address', '=', False)]) if remaining: msg = _('%d address(es) resolved. %d still need geocoding — click "Geocode Next %d" again to continue.') % ( @@ -193,11 +154,8 @@ class WtLocationLog(models.Model): } def action_geocode_all_pending(self): - """Action to geocode the next batch of ALL unresolved records (ignores selection).""" - pending = self.search([ - ('latitude', '!=', 0), - ('address', '=', False), - ], limit=GEOCODE_BATCH_SIZE) + """Geocode the next batch of ALL unresolved records (ignores selection).""" + pending = self.search([('latitude', '!=', 0), ('address', '=', False)], limit=GEOCODE_BATCH_SIZE) if not pending: return { 'type': 'ir.actions.client', @@ -220,8 +178,7 @@ class WtLocationLog(models.Model): for log in logs: if prev and prev.latitude and log.latitude: log.distance_from_previous = self._haversine_distance( - prev.latitude, prev.longitude, - log.latitude, log.longitude + prev.latitude, prev.longitude, log.latitude, log.longitude ) if prev.departed_at and log.arrived_at: delta = log.arrived_at - prev.departed_at