import requests import logging import json import base64 import uuid from datetime import datetime from flask import Flask, jsonify, request from threading import Thread import time from flask_cors import CORS app = Flask(__name__) CORS(app) # This will enable CORS for all routes # Constants UPDATE_INTERVAL = 60 * 10 # 10 minutes UPS_CLIENT_ID = 'LKrZBgcSZ2v7vtAn3VAmEEBXqpkXD8zIEcZum97Eb8sSl7Fx' UPS_CLIENT_SECRET = 'DBAV77mzTWgI5oqWwXlA12hi62hgyUkesoGsREZsmjd1WHMSugpDUuBGTjyUiteT' FEDEX_API_KEY = 'l705da56bf34254c3495f351772a6ea2d3' FEDEX_SECRET_KEY = 'ae27688af74341f094228fc67a36a8cc' # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # Functions to get access tokens def get_fedex_access_token(): url = "https://apis.fedex.com/oauth/token" headers = {'Content-Type': "application/x-www-form-urlencoded"} payload = { 'grant_type': 'client_credentials', 'client_id': FEDEX_API_KEY, 'client_secret': FEDEX_SECRET_KEY } try: logger.info("Requesting FedEx token") response = requests.post(url, data=payload, headers=headers) response.raise_for_status() token_data = response.json() return token_data['access_token'] except requests.exceptions.RequestException as e: logger.error(f"Error obtaining FedEx access token: {e}") if hasattr(e, 'response'): logger.error(f"Response status code: {e.response.status_code}") logger.error(f"Response headers: {dict(e.response.headers)}") logger.error(f"Response content: {e.response.text}") raise def get_ups_access_token(): token_url = "https://onlinetools.ups.com/security/v1/oauth/token" headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'User-Agent': 'OCMTrackingApp/1.0', 'transId': str(uuid.uuid4()), 'transactionSrc': 'OCMTrackingApp' } credentials = base64.b64encode(f"{UPS_CLIENT_ID}:{UPS_CLIENT_SECRET}".encode()).decode() headers['Authorization'] = f'Basic {credentials}' data = {'grant_type': 'client_credentials'} try: logger.info("Requesting UPS token") response = requests.post(token_url, headers=headers, data=data) response.raise_for_status() token_data = response.json() return token_data['access_token'] except requests.exceptions.RequestException as e: logger.error(f"Error obtaining UPS access token: {e}") if hasattr(e, 'response'): logger.error(f"Response content: {e.response.text}") raise # Functions to fetch tracking info def fetch_fedex_tracking_info(tracking_number): access_token = get_fedex_access_token() url = "https://apis.fedex.com/track/v1/trackingnumbers" headers = { 'Content-Type': "application/json", 'X-locale': "en_US", 'Authorization': f"Bearer {access_token}" } payload = { "trackingInfo": [ {"trackingNumberInfo": {"trackingNumber": tracking_number}} ], "includeDetailedScans": True } try: logger.info(f"Requesting FedEx tracking info for {tracking_number}") response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() track_results = data['output']['completeTrackResults'][0]['trackResults'][0] latest_status = track_results['latestStatusDetail'] scan_events = track_results.get('scanEvents', []) def format_date(date_str): if not date_str or date_str == 'N/A': return 'N/A' try: date = datetime.fromisoformat(date_str.replace('Z', '+00:00')) return date.strftime('%m/%d/%Y %I:%M %p') except ValueError: logger.warning(f"Unable to parse date: {date_str}") return 'N/A' estimated_delivery = 'N/A' if 'standardTransitTimeWindow' in track_results and 'window' in track_results['standardTransitTimeWindow'] and 'ends' in track_results['standardTransitTimeWindow']['window']: estimated_delivery = format_date(track_results['standardTransitTimeWindow']['window']['ends']) elif 'estimatedDeliveryTimeWindow' in track_results and 'window' in track_results['estimatedDeliveryTimeWindow'] and 'ends' in track_results['estimatedDeliveryTimeWindow']['window']: estimated_delivery = format_date(track_results['estimatedDeliveryTimeWindow']['window']['ends']) if 'scanLocation' in latest_status and 'city' in latest_status['scanLocation']: location = f"{latest_status['scanLocation']['city']}, {latest_status['scanLocation'].get('stateOrProvinceCode', 'N/A')}" else: location = 'N/A' latest_event_time = format_date(latest_status.get('scanDateTime')) if latest_event_time == 'N/A' and scan_events: latest_event_time = format_date(scan_events[0].get('date')) delivery_time = 'N/A' if latest_status.get('statusByLocale') == 'Delivered': for date_time in track_results.get('dateAndTimes', []): if date_time.get('type') == 'ACTUAL_DELIVERY': delivery_time = format_date(date_time.get('dateTime')) break latest_checkpoint = latest_status.get('description', 'N/A') if latest_status.get('code') == 'SE': reason_description = latest_status.get('ancillaryDetails', [{}])[0].get('reasonDescription', '') if reason_description: latest_checkpoint += f" ({reason_description})" last_update_date_time = delivery_time if delivery_time != 'N/A' else latest_event_time return { 'status': latest_status.get('statusByLocale', 'N/A'), 'courier': 'FedEx', 'estimatedDelivery': estimated_delivery, 'latestLocation': location, 'latestCheckpoint': latest_checkpoint, 'lastUpdateDateTime': last_update_date_time, } except requests.exceptions.RequestException as e: logger.error(f"Error fetching FedEx tracking info: {e}") if hasattr(e, 'response'): logger.error(f"Response status code: {e.response.status_code}") logger.error(f"Response headers: {dict(e.response.headers)}") logger.error(f"Response content: {e.response.text}") raise except (KeyError, IndexError) as e: logger.error(f"Error parsing FedEx tracking info: {e}") logger.error(f"Response content: {data}") raise def fetch_ups_tracking_info(tracking_number): access_token = get_ups_access_token() url = f"https://onlinetools.ups.com/api/track/v1/details/{tracking_number}" headers = { 'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'OCMTrackingApp/1.0', 'transId': str(uuid.uuid4()), 'transactionSrc': 'OCMTrackingApp' } params = { "locale": "en_US", "returnSignature": "false", "returnMilestones": "false", "returnPOD": "false" } try: logger.info(f"Requesting UPS tracking info for {tracking_number}") response = requests.get(url, headers=headers, params=params) response.raise_for_status() data = response.json() shipment = data['trackResponse']['shipment'][0] package = shipment['package'][0] events = package.get('activity', []) if events: sorted_events = sorted(events, key=lambda x: x['date'] + x['time'], reverse=True) latest_event = sorted_events[0] else: latest_event = None def format_date(date_str, time_str): if not date_str or not time_str or date_str == 'N/A' or time_str == 'N/A': return 'N/A' try: date = datetime.strptime(date_str + time_str, '%Y%m%d%H%M%S') return date.strftime('%m/%d/%Y %I:%M %p') except ValueError: logger.warning(f"Unable to parse date: {date_str} or time: {time_str}") return 'N/A' delivery_date = 'N/A' delivery_time = 'N/A' if 'deliveryDate' in package: if isinstance(package['deliveryDate'], list) and package['deliveryDate']: delivery_date = package['deliveryDate'][0].get('date', 'N/A') elif isinstance(package['deliveryDate'], dict): delivery_date = package['deliveryDate'].get('date', 'N/A') if 'deliveryTime' in package: delivery_time = package['deliveryTime'].get('endTime', 'N/A') return { 'status': latest_event['status']['type'] if latest_event else 'N/A', 'courier': 'UPS', 'estimatedDelivery': format_date(delivery_date, delivery_time), 'latestLocation': latest_event['location']['address'].get('city', 'N/A') if latest_event else 'N/A', 'latestCheckpoint': latest_event['status']['description'] if latest_event else 'N/A', 'lastUpdateDateTime': format_date(latest_event['date'], latest_event['time']) if latest_event else 'N/A', } except requests.exceptions.RequestException as e: logger.error(f"Error fetching UPS tracking info: {e}") if hasattr(e, 'response'): logger.error(f"Response content: {e.response.text}") raise @app.route('/') def home(): return "Tracking update service is running." @app.route('/ups', methods=['POST']) def get_ups_tracking(): tracking_number = request.json.get('tracking_number') if not tracking_number: return jsonify({"status": "error", "message": "Tracking number is required"}), 400 try: tracking_info = fetch_ups_tracking_info(tracking_number) return jsonify(tracking_info), 200 except Exception as e: logger.error(f"Error in get_ups_tracking: {e}") return jsonify({"status": "error", "message": str(e)}), 500 @app.route('/fedex', methods=['POST']) def get_fedex_tracking(): tracking_number = request.json.get('tracking_number') if not tracking_number: return jsonify({"status": "error", "message": "Tracking number is required"}), 400 try: tracking_info = fetch_fedex_tracking_info(tracking_number) return jsonify(tracking_info), 200 except Exception as e: logger.error(f"Error in get_fedex_tracking: {e}") return jsonify({"status": "error", "message": str(e)}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=7860)