Code Snippets: My Approach & Problem Solving
This section offers a glimpse into my coding style, problem-solving techniques, and a few reusable patterns. I believe in clean, efficient, and well-documented code that addresses real-world business needs.
Outlook Attachment Scraper with Advanced Logging and Graceful Exit (Python + win32com)
This script demonstrates robust automation for managing email attachments. Key problem-solving aspects include: handling file duplicates by moving them to a dedicated folder, implementing comprehensive logging for traceability and debugging, and enabling a graceful exit mechanism via a separate thread for user control during long-running operations. This ensures data integrity and operational resilience for critical compliance and data management tasks.
import os
import win32com.client
from datetime import datetime
import uuid
import time
import threading
import logging
import traceback
# Set base folder
save_folder = r"Desired\Path\To\Save\Attachments"
# Create necessary folders
duplicate_folder = os.path.join(save_folder, "Duplicate")
log_folder = os.path.join(save_folder, "Log")
os.makedirs(duplicate_folder, exist_ok=True)
os.makedirs(log_folder, exist_ok=True)
# Configure logging
log_filename = os.path.join(log_folder, f"log_{datetime.now().strftime('%Y-%m-%d')}.txt")
logging.basicConfig(
filename=log_filename,
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logging.info("Script started.")
# Initialize Outlook
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
def process_attachments():
# Locate the 'Organized' folder
organized_folder = None
for folder in inbox.Folders:
if folder.Name == "Organized":
organized_folder = folder
break
# Locate the 'PM' folder
PM_DOCS = None
if organized_folder:
for folder in organized_folder.Folders:
if folder.Name == "PM_Data":
PM_DOCS = folder
break
# Locate or create 'Processed' folder
processed_folder = None
for folder in inbox.Folders:
if folder.Name == "Processed":
processed_folder = folder
break
if not processed_folder:
processed_folder = inbox.Folders.Add("Processed")
if PM_DOCS:
items_to_process = list(PM_DOCS.Items)
processed_count = 0
for item in items_to_process:
if item.Class == 43 and item.Attachments.Count > 0:
processed = False
for attachment in item.Attachments:
if attachment.Type == 1 and attachment.FileName.lower().endswith(".pdf"):
attachment_filename = attachment.FileName
attachment_path = os.path.join(save_folder, attachment_filename)
# Create unique file name
unique_id = uuid.uuid4().hex[:8]
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
file_name, file_extension = os.path.splitext(attachment_filename)
new_attachment_filename = f"{file_name}_{timestamp}_{unique_id}{file_extension}"
# Save to correct location
if os.path.exists(attachment_path):
# Duplicate
new_attachment_path = os.path.join(duplicate_folder, new_attachment_filename)
attachment.SaveAsFile(new_attachment_path)
logging.info(f"Duplicate file saved: {new_attachment_path}")
else:
# Original
temp_path = os.path.join(save_folder, attachment_filename)
attachment.SaveAsFile(temp_path)
new_attachment_path = os.path.join(save_folder, new_attachment_filename)
os.rename(temp_path, new_attachment_path)
logging.info(f"Processed file: {new_attachment_path}")
processed = True
if processed:
item.Move(processed_folder)
processed_count += 1
logging.info(f"Checked and processed {processed_count} emails.")
else:
logging.warning("Folder structure not found'.")
# Flag to stop the loop
stop_flag = False
def monitor_input():
global stop_flag
print("Type 'exit' to stop the script:")
buffer = ""
import msvcrt
while not stop_flag:
if msvcrt.kbhit():
char = msvcrt.getwche()
if char in ('\r', '\n'):
if buffer.strip().lower() == "exit":
print("\nExiting...")
stop_flag = True
buffer = ""
else:
buffer += char
time.sleep(0.1)
# Start input monitor thread
input_thread = threading.Thread(target=monitor_input, daemon=True)
input_thread.start()
# Main loop
while not stop_flag:
try:
process_attachments()
except Exception as e:
logging.error(f"Error occurred: {str(e)}")
logging.error(traceback.format_exc())
for _ in range(60): # Sleep in 10s intervals for responsiveness
if stop_flag:
break
time.sleep(10)
logging.info("Script has stopped.")
print("Script has stopped.")
FMS API CLI Tool for Batch Equipment Updates (Python + `aiohttp`, `pandas`)
This CLI tool demonstrates comprehensive problem-solving for data management with an external API. It includes a flexible field mapping system for user-friendly data input, a critical dry-run mode to prevent accidental live system changes, and robust error handling with detailed logging for auditing and debugging. The use of `aiohttp` and `tqdm` also highlights efficient handling of multiple asynchronous API requests and user feedback during batch operations.
import asyncio
import aiohttp
import pandas as pd
from tqdm.asyncio import tqdm
import logging
from getpass import getpass
import os
import csv
from pathlib import Path
from datetime import datetime
# Mapping of human-readable field names to integration names
FIELD_MAP = {
"Accessory Last Annual PM": "accessory_last_annual_pm",
"Accessory Next Annual PM": "accessory_next_annual_pm",
"Accessory Last PM": "accessory_last_pm",
"Accessory Next PM": "accessory_next_pm",
"Chassis Last Annual PM": "Last_Annual_PM",
"Chassis Next Annual PM Due Date": "Next_Annual_PM_Due_Date",
"Chassis Last PM": "Last_PM",
"Chassis Next PM Due Date": "Next_PM_Due_Date",
"Color": "Color",
"Engine Displacement": "Engine_Displacement",
"Engine Family Name": "Engine_Family_Name",
"Engine Make": "Engine_Make",
"Engine Model": "Engine_Model",
"Engine Notification Value": "Engine_Notification_Value",
"Engine Serial #": "Engine_Serial_Num",
"Engine Year": "Engine_Year",
"GeoTab ID": "GeoTab_ID",
"Camera IMEI": "Camera_IMEI",
"Geotab Serial Number": "Geotab_Serial_Number",
"GVW": "GVW",
"License Exp": "License_Exp",
"License Expiration": "License_Expiration",
"License Plate": "License",
"License State": "state",
"License Type": "License_Type",
"Title": "Title",
"Toll Tag #": "Toll_Tag_Num",
"Toll Tag Effective Date": "Toll_Tag_Effective_Date",
"Toll Tag Expiration": "Toll_Tag_Expiration",
}
def prompt_credentials():
print("๐ Enter your credentials:")
login_name = input("Login Name: ").strip()
password = getpass("Password: ").strip()
cust_id = input("Customer ID: ").strip()
return login_name, password, cust_id
def prompt_field_selection():
print("\n๐ Select fields to update (comma-separated numbers):")
for i, field in enumerate(FIELD_MAP.keys(), 1):
print(f"{i}. {field}")
selection = input("Enter numbers (e.g. 1,3,5): ").strip()
try:
selected = [list(FIELD_MAP.keys())[int(i) - 1] for i in selection.split(",") if i.strip().isdigit()]
return selected
except (IndexError, ValueError):
print("โ ๏ธ Invalid selection. Please enter comma-separated numbers.")
return prompt_field_selection()
def sanitize_path(path: str) -> str:
return path.strip().strip('"').strip("'").strip()
def prompt_excel_path():
return sanitize_path(input('\n๐ Enter path to Excel file: '))
def prompt_generate_template(selected_fields):
answer = input("\n๐ Generate Excel template with required columns? (y/n): ").strip().lower()
return answer == "y"
def get_default_template_path():
downloads = Path.home() / "Downloads"
downloads.mkdir(exist_ok=True) # ensure it exists
return str(downloads / "FMS_UPDATE_TEMPLATE.xlsx")
def create_excel_template(path, selected_fields, overwrite=False):
headers = ['equipment_id'] + selected_fields
df = pd.DataFrame(columns=headers)
if Path(path).exists() and not overwrite:
print(f"โ ๏ธ File already exists: {path}")
confirm = input("Overwrite? (y/n): ").strip().lower()
if confirm != 'y':
print("๐ซ Skipping template creation.")
return
df.to_excel(path, index=False)
print(f"โ
Template saved to: {path}")
async def login(login_name, password, cust_id):
login_url = "UPDATE_URL_HERE"
headers = {
'loginName': login_name,
'password': password,
'custId': cust_id
}
async with aiohttp.ClientSession() as session:
async with session.get(login_url, headers=headers) as response:
text = await response.text()
if response.status != 200 or "" not in text:
raise Exception("Login failed. Response:\n" + text)
session_id = text.split("")[1].split(" ")[0]
print("โ
Logged in successfully.")
return session_id
def read_excel(file_path, selected_fields):
df = pd.read_excel(file_path)
df.columns = [str(col).strip() for col in df.columns] # Normalize headers
expected_cols = ['equipment_id'] + selected_fields
missing = [col for col in expected_cols if col not in df.columns]
if missing:
raise ValueError(f"โ Missing required columns in Excel: {missing}")
print(f"โ
Found {len(df)} records in Excel")
updates = {}
for _, row in df.iterrows():
equipment_id = row['equipment_id']
update_fields = {FIELD_MAP[f]: row[f] for f in selected_fields if pd.notna(row[f])}
if update_fields:
updates[equipment_id] = update_fields
return updates
async def update_record(session, base_url, session_id, equipment_id, fields, log_writer, dry_run=False):
params = {
"sessionId": session_id,
"objName": "Equipment1",
"id": equipment_id,
"useIds": "false"
}
params.update({k: str(v) for k, v in fields.items()})
if dry_run:
status = "โ
Dry run - no update sent"
log_writer.writerow([equipment_id, "DRY_RUN", "Success", status])
return
try:
async with session.post(base_url, params=params) as response:
text = await response.text()
status_code = response.status
result = "Success" if status_code == 200 else "Failed"
log_writer.writerow([equipment_id, status_code, result, text])
except Exception as e:
log_writer.writerow([equipment_id, "ERROR", "Failed", str(e)])
async def send_updates(updates, session_id, dry_run=False):
base_url = "UPDATE_URL_HERE" # Replace with actual update URL
log_file = "update_log.csv"
with open(log_file, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["equipment_id", "response_code", "status", "details"])
async with aiohttp.ClientSession() as session:
for equipment_id, fields in tqdm(updates.items(), desc="๐ Sending updates"):
await update_record(session, base_url, session_id, equipment_id, fields, writer, dry_run=dry_run)
print(f"๐งพ Log saved to: {log_file}")
async def main():
print("๐ Enter your credentials:")
login_name = input("Login Name: ").strip()
password = getpass("Password: ").strip()
cust_id = input("Customer ID: ").strip()
selected_fields = prompt_field_selection()
if input("๐ Would you like to generate an Excel template for the selected fields? (y/n): ").lower() == "y":
template_path = get_default_template_path()
overwrite = input("โ ๏ธ Overwrite if file exists? (y/n): ").lower() == "y"
create_excel_template(template_path, selected_fields, overwrite=overwrite)
# return
dry_run = input("๐ Dry run (no updates sent)? (y/n): ").lower() == "y"
try:
session_id = await login(login_name, password, cust_id)
print("โ
Logged in successfully.")
except Exception as e:
print(f"โ Login failed: {e}")
return
file_path = input("๐ Enter path to Excel file: ").strip().strip('"')
try:
updates = read_excel(file_path, selected_fields)
if not updates:
print("โ ๏ธ No updates to process. Check your Excel data.")
return
print(f"๐ฆ Prepared {len(updates)} records for update.")
except Exception as e:
print(f"โ Failed to read Excel file: {e}")
return
await send_updates(updates, session_id, dry_run=dry_run)
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n๐ Operation cancelled by user.")
VIN Decoder Desktop Application (Python + Tkinter)
This desktop application provides a user-friendly interface for VIN decoding by integrating with the NHTSA API. Problem-solving is evident in its clear GUI design for ease of use, robust error handling for API requests, and dynamic updating of result fields, which gracefully manages the display of varying data returned from the API. This tool simplifies a complex data retrieval process for end-users.
import tkinter as tk
import requests
import xml.dom.minidom
import tkinter.messagebox as messagebox
class VinDecoderApp:
def __init__(self, root):
self.root = root
self.root.title("VIN Decoder App")
bground = '#ffffff'
fground = '#000000'
tkfont = ("Areial", 12)
btnfont = ("Areial", 12, "bold")
warnfont = ("Areial", 10, "bold")
root.configure(bg=bground)
# Label for VIN entry
self.vin_label = tk.Label(root, text="Enter VIN", font=("Areial",14,'bold'), bg=bground, fg=fground)
self.vin_label.grid(row=0, column=0, pady=10, padx=10, columnspan=1, sticky="w")
# Entry for VIN input
self.vin_entry = tk.Entry(root, width=30)
self.vin_entry.grid(row=0, column=1, pady=5, padx=10, columnspan=4, sticky="ew")
# Fields to display VIN information
self.result_fields = {}
fields_column1 = ["Manufacturer", "ModelYear", "Make", "VIN", "Axles", "BodyCabType", "DisplacementCC", "EngineModel",
"GVWR", "PlantCountry", "VehicleType", "BrakeSystemType", "TransmissionStyle", "WheelBaseLong", "Wheels"]
fields_column2 = ["Model", "BodyClass", "DriveType", "FuelTypePrimary", "PlantCompanyName", "PlantState", "CurbWeightLB",
"WheelBaseShort", "WheelBaseType", "WheelSizeFront", "WheelSizeRear", "ErrorText"]
for i, (field1, field2) in enumerate(zip(fields_column1, fields_column2)):
label1 = tk.Label(root, text=field1, bg=bground, fg=fground, font=tkfont)
label1.grid(row=i + 2, column=0, pady=8, padx=10, sticky="e")
entry1 = tk.Entry(root, state="readonly", width=40)
entry1.grid(row=i + 2, column=1, pady=8, padx=10, sticky="w")
self.result_fields[field1] = entry1
label2 = tk.Label(root, text=field2, bg=bground, fg=fground, font=tkfont)
label2.grid(row=i + 2, column=2, pady=8, padx=10, sticky="e")
entry2 = tk.Entry(root, state="readonly", width=40)
entry2.grid(row=i + 2, column=3, pady=8, padx=10, sticky="w")
self.result_fields[field2] = entry2
# Button to fetch and display VIN information
self.fetch_button = tk.Button(root, text="Decode Vin", font=btnfont, command=self.fetch_and_display)
self.fetch_button.grid(row=i + 3, columnspan=2, column=0, pady=10, padx=10, sticky="ew")
# Button to clear VIN entry and results
self.clear_button = tk.Button(root, text="Clear", font=btnfont, command=self.clear_entries)
self.clear_button.grid(row=i + 3, columnspan=2, column=2, pady=10, padx=10, sticky="ew")
# Warning Label
self.vin_label = tk.Label(root, font=warnfont, text="This application is designed to access VIN data from the NHTSA website.", bg=bground, fg=fground)
self.vin_label.grid(row=i + 4, column=0, pady=10, padx=10, columnspan=4, sticky="ew")
# Warning Label
self.vin_label = tk.Label(root, font=warnfont, text="Please exercise caution when interpreting results, as variations in our equipment may not align precisely with the manufacturer's information.", bg=bground, fg=fground)
self.vin_label.grid(row=i + 5, column=0, pady=10, padx=10, columnspan=4, sticky="ew")
def fetch_and_display(self):
self.fetch_button.configure(state="disabled")
# Clear out prior entries
self.clear_result_fields()
vin = self.vin_entry.get().strip()
api_url = "https://vpic.nhtsa.dot.gov/api/vehicles/decodevinvalues/"
try:
response = self.make_api_request(api_url + vin)
self.update_result_fields(response)
except requests.exceptions.RequestException as e:
error_message = f"Error fetching data: {str(e)}"
self.display_error_message(error_message)
finally:
# Enables the fetch button.
self.fetch_button.configure(state="normal")
def make_api_request(self, url):
with requests.Session() as session:
response = session.get(url, verify=True)
response.raise_for_status()
return response
def update_result_fields(self, response):
dom = xml.dom.minidom.parseString(response.text)
# Update result fields with VIN information
for field, entry in self.result_fields.items():
elements = dom.getElementsByTagName(field)
if elements and elements[0].firstChild:
value = elements[0].firstChild.nodeValue.strip()
else:
value = "N/A"
entry.configure(state="normal")
entry.delete(0, tk.END)
entry.insert(0, value)
entry.configure(state="readonly")
def display_error_message(self, message):
messagebox.showinfo("Error", message)
def clear_entries(self):
# Clear VIN entry and result fields
self.vin_entry.delete(0, tk.END)
self.clear_result_fields()
def clear_result_fields(self):
for entry in self.result_fields.values():
entry.configure(state="normal")
entry.delete(0, tk.END)
entry.configure(state="readonly")
if __name__ == "__main__":
root = tk.Tk()
app = VinDecoderApp(root)
root.mainloop()
Fleet Innovation Admin Tool - Authentication and Device Management (HTML/JavaScript)
This HTML/JavaScript snippet showcases problem-solving in building an interactive web-based administration tool. It includes a clear authentication mechanism for secure API access and a structured approach to fetching and displaying device information. The code addresses user experience by providing visual feedback on authentication status and by dynamically rendering data, offering a practical solution for managing telematics devices directly from a web browser.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FIAT</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://cdn.muicss.com/mui-0.10.3/css/mui.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<style>
/* Variables */
/* Variables */
:root {
--primary-color: #4a90e2;
--accent-color: #ff6e40;
--error-color: #d32f2f;
--success-color: #4caf50;
--light-bg: #f5f7fa;
--light-container: #ffffff;
--dark-bg: #121212;
--dark-container: #1e1e1e;
--border-radius: 8px;
--box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
/* Base Styles */
body {
font-family: 'Roboto', sans-serif;
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: var(--light-bg);
color: #333;
line-height: 1.6;
}
.container {
max-width: 900px;
margin: 30px auto;
padding: 25px;
background-color: var(--light-container);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
}
h1,
h2 {
color: var(--primary-color);
text-align: center;
margin-bottom: 25px;
}
.mui-textfield input,
.mui-textfield textarea,
.mui-btn {
border-radius: 5px;
}
.mui-btn-primary {
background-color: var(--primary-color);
}
.mui-btn-danger {
background-color: var(--error-color);
}
.mui-btn {
transition: var(--transition);
}
.mui-btn:hover {
opacity: 0.9;
}
.mui-panel {
margin-bottom: 25px;
border-radius: var(--border-radius);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
padding: 20px;
background-color: #fff;
}
.status-message {
margin-top: 15px;
padding: 10px 15px;
border-radius: 5px;
font-weight: bold;
text-align: center;
}
.status-message.success {
background-color: #e8f5e9;
color: var(--success-color);
border: 1px solid #c8e6c9;
}
.status-message.error {
background-color: #ffebee;
color: var(--error-color);
border: 1px solid #ffcdd2;
}
.status-message.info {
background-color: #e3f2fd;
color: #2196f3;
border: 1px solid #bbdefb;
}
/* QR Code */
#qrcode {
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
margin-top: 20px;
border: 1px dashed #ccc;
border-radius: 5px;
min-height: 150px;
box-sizing: border-box;
}
#qrcode img {
max-width: 100%;
height: auto;
}
/* Responsive adjustments */
@media (max-width: 600px) {
.container {
margin: 15px;
padding: 15px;
}
}
/* Dark Mode Toggle (for future expansion) */
body.dark-mode {
background-color: var(--dark-bg);
color: #f5f5f5;
}
body.dark-mode .container,
body.dark-mode .mui-panel {
background-color: var(--dark-container);
color: #f5f5f5;
}
body.dark-mode .mui-textfield input,
body.dark-mode .mui-textfield textarea {
color: #f5f5f5;
background-color: #333;
border-color: #555;
}
body.dark-mode .mui-textfield label {
color: #bbb;
}
</style>
</head>
<body>
<div class="mui-appbar">
<div class="mui-container">
<table width="100%">
<tr style="vertical-align: middle;">
<td class="mui--appbar-height">
<a href="index.html" style="color: white; text-decoration: none; font-size: 24px; font-weight: bold;">
FIAT
</a>
</td>
</tr>
</table>
</div>
</div>
<div class="mui-container">
<!-- Authentication Section -->
<div class="mui-panel">
<h2>๐ Authenticate</h2>
<div class="mui-textfield">
<input type="password" id="api-key" placeholder="Enter API Key">
<label for="api-key">API Key</label>
</div>
<button class="mui-btn mui-btn-primary" onclick="authenticate()">Authenticate</button>
<div id="auth-status" class="status-message"></div>
</div>
<!-- Device Information Section -->
<div class="mui-panel">
<h2>๐
Telematics Map for Asset Visualization (HTML/JavaScript)
This HTML/JavaScript code demonstrates a solution for visualizing asset locations on a map using the Leaflet.js library and integrating with an API to fetch telematics data. The problem solved here is presenting complex geographical data in an intuitive and interactive way, allowing users to quickly grasp the distribution and status of assets. The dynamic loading of data and the mapping functionality are key features.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Telematics Map</title>
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<style>
body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
#map { height: 80vh; width: 100%; }
#controls {
padding: 10px;
background: white;
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
border-radius: 5px;
box-shadow: 0 0 15px rgba(0,0,0,0.2);
display: flex;
gap: 10px;
align-items: center;
}
select, button {
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
font-size: 14px;
}
button {
background-color: #007bff;
color: white;
cursor: pointer;
border: none;
}
button:hover {
background-color: #0056b3;
}
.loading-spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 2s linear infinite;
display: none; /* Hidden by default */
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.marker-popup {
font-family: Arial, sans-serif;
font-size: 14px;
}
.marker-popup h4 {
margin: 0 0 5px 0;
color: #007bff;
}
.marker-popup p {
margin: 0 0 3px 0;
}
.marker-popup strong {
color: #333;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="controls">
<label for="unit-select">Select Unit:</label>
<select id="unit-select">
<option value="">All Units</option>
<!-- Options will be loaded dynamically -->
</select>
<button onclick="fetchTelematicsData()">Refresh Map</button>
<div id="loading-spinner" class="loading-spinner"></div>
</div>
<!-- Leaflet JavaScript -->
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script>
let map;
let markers = L.featureGroup();
let allUnitsData = []; // To store all fetched data
// Initialize map
function initMap() {
map = L.map('map').setView([39.8283, -98.5795], 4); // Centered on USA
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
markers.addTo(map);
}
// Fetch telematics data from API
async function fetchTelematicsData() {
const spinner = document.getElementById('loading-spinner');
spinner.style.display = 'block'; // Show spinner
const unitSelect = document.getElementById('unit-select');
const selectedUnit = unitSelect.value;
try {
// Replace with your actual API endpoint and API key handling
const response = await fetch('YOUR_TELEMATICS_API_ENDPOINT', {
headers: {
'Authorization': 'Bearer YOUR_API_KEY' // Or whatever your auth method is
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
allUnitsData = data; // Store all data
populateUnitDropdown(data);
updateMap(selectedUnit ? data.filter(d => d.unitId === selectedUnit) : data);
} catch (error) {
console.error("Error fetching telematics data:", error);
alert("Failed to load telematics data. Please check the console for details.");
} finally {
spinner.style.display = 'none'; // Hide spinner
}
}
// Populate the unit dropdown
function populateUnitDropdown(data) {
const unitSelect = document.getElementById('unit-select');
const currentSelectedValue = unitSelect.value; // Preserve current selection
unitSelect.innerHTML = '<option value="">All Units</option>'; // Clear existing options
const uniqueUnits = [...new Set(data.map(d => d.unitId))].sort();
uniqueUnits.forEach(unitId => {
const option = document.createElement('option');
option.value = unitId;
option.textContent = unitId;
unitSelect.appendChild(option);
});
// Restore previous selection or default to "All Units"
if (uniqueUnits.includes(currentSelectedValue)) {
unitSelect.value = currentSelectedValue;
} else {
unitSelect.value = "";
}
unitSelect.onchange = () => {
const newSelectedUnit = unitSelect.value;
updateMap(newSelectedUnit ? allUnitsData.filter(d => d.unitId === newSelectedUnit) : allUnitsData);
};
}
// Update map markers
function updateMap(data) {
markers.clearLayers(); // Clear existing markers
if (data.length === 0) {
console.log("No data to display on map.");
return;
}
data.forEach(item => {
if (item.latitude && item.longitude) {
const popupContent = `
<div class="marker-popup">
<h4>Unit ID: ${item.unitId}</h4>
<p><strong>Location:</strong> ${item.latitude.toFixed(4)}, ${item.longitude.toFixed(4)}</p>
<p><strong>Speed:</strong> ${item.speed || 'N/A'} mph</p>
<p><strong>Last Update:</strong> ${new Date(item.timestamp).toLocaleString()}</p>
<p><strong>Status:</strong> ${item.status || 'N/A'}</p>
</div>
`;
const marker = L.marker([item.latitude, item.longitude])
.bindPopup(popupContent);
markers.addLayer(marker);
}
});
// Fit map to markers if there are any
if (markers.getLayers().length > 0) {
map.fitBounds(markers.getBounds());
} else {
// If no markers, reset view to USA
map.setView([39.8283, -98.5795], 4);
}
}
// Initialize map and fetch data on load
document.addEventListener('DOMContentLoaded', () => {
initMap();
fetchTelematicsData(); // Initial data fetch
// Set interval for refreshing data, e.g., every 5 minutes (300000 ms)
// setInterval(fetchTelematicsData, 300000);
});
</script>
</body>
</html>