from datetime import datetime, timedelta, timezone from typing import Any, Dict, List, Optional, Tuple, Union import gpxpy.gpx from ..exceptions import InvalidGPXException, WorkoutGPXException from .weather import WeatherService weather_service = WeatherService() def open_gpx_file(gpx_file: str) -> Optional[gpxpy.gpx.GPX]: gpx_file = open(gpx_file, 'r') # type: ignore gpx = gpxpy.parse(gpx_file) if len(gpx.tracks) == 0: return None return gpx def get_gpx_data( parsed_gpx: Union[gpxpy.gpx.GPX, gpxpy.gpx.GPXTrackSegment], max_speed: float, start: Union[datetime, None], stopped_time_between_seg: timedelta, stopped_speed_threshold: float, ) -> Dict: """ Returns data from parsed gpx file """ gpx_data: Dict[str, Any] = { 'max_speed': (max_speed / 1000) * 3600, 'start': start, } duration = parsed_gpx.get_duration() gpx_data['duration'] = ( timedelta(seconds=duration if duration else 0) + stopped_time_between_seg ) ele = parsed_gpx.get_elevation_extremes() gpx_data['elevation_max'] = ele.maximum gpx_data['elevation_min'] = ele.minimum hill = parsed_gpx.get_uphill_downhill() gpx_data['uphill'] = hill.uphill gpx_data['downhill'] = hill.downhill moving_data = parsed_gpx.get_moving_data( stopped_speed_threshold=stopped_speed_threshold ) if moving_data: gpx_data['moving_time'] = timedelta(seconds=moving_data.moving_time) gpx_data['stop_time'] = ( timedelta(seconds=moving_data.stopped_time) + stopped_time_between_seg ) distance = moving_data.moving_distance + moving_data.stopped_distance gpx_data['distance'] = distance / 1000 average_speed = ( distance / moving_data.moving_time if moving_data.moving_time > 0 else 0 ) gpx_data['average_speed'] = (average_speed / 1000) * 3600 return gpx_data def get_gpx_info( gpx_file: str, stopped_speed_threshold: float, update_map_data: Optional[bool] = True, update_weather_data: Optional[bool] = True, use_raw_gpx_speed: Optional[bool] = False ) -> Tuple: """ Parse and return gpx, map and weather data from gpx file """ try: gpx = open_gpx_file(gpx_file) except Exception: raise InvalidGPXException('error', 'gpx file is invalid') if gpx is None: raise InvalidGPXException('error', 'no tracks in gpx file') gpx_data: Dict = {'name': gpx.tracks[0].name, 'segments': []} max_speed = 0.0 start: Optional[datetime] = None map_data = [] weather_data = [] segments_nb = len(gpx.tracks[0].segments) prev_seg_last_point = None no_stopped_time = timedelta(seconds=0) stopped_time_between_seg = no_stopped_time for segment_idx, segment in enumerate(gpx.tracks[0].segments): segment_start: Optional[datetime] = None segment_points_nb = len(segment.points) for point_idx, point in enumerate(segment.points): if point.time is None: raise InvalidGPXException( 'error', '