updated to pauses

This commit is contained in:
2025-06-07 21:08:23 +03:00
parent 0ebdbc6b74
commit fa3a11ee4b
17 changed files with 604 additions and 203 deletions

185
utils.py
View File

@@ -2,7 +2,8 @@ import os
import json
import requests
from cryptography.fernet import Fernet
import math
import datetime
RESOURCES_FOLDER = "resources"
CREDENTIALS_FILE = os.path.join(RESOURCES_FOLDER, "credentials.enc")
KEY_FILE = os.path.join(RESOURCES_FOLDER, "key.key")
@@ -306,3 +307,185 @@ def process_preview_util(
def close_popup(dt):
popup.dismiss()
Clock.schedule_once(close_popup, 2)
def haversine(lat1, lon1, lat2, lon2):
# Returns distance in meters between two lat/lon points
R = 6371000 # Earth radius in meters
phi1 = math.radians(lat1)
phi2 = math.radians(lat2)
dphi = math.radians(lat2 - lat1)
dlambda = math.radians(lon2 - lon1)
a = math.sin(dphi/2)**2 + math.cos(phi1)*math.cos(phi2)*math.sin(dlambda/2)**2
return 2 * R * math.asin(math.sqrt(a))
def optimize_route_entries_util(
project_name,
RESOURCES_FOLDER,
label,
progress,
popup,
Clock,
on_save=None
):
def process_entries(dt):
project_folder = os.path.join(RESOURCES_FOLDER, "projects", project_name)
positions_path = os.path.join(project_folder, "positions.json")
pauses_path = os.path.join(project_folder, "pauses.json")
if not os.path.exists(positions_path):
label.text = "positions.json not found!"
progress.value = 100
return
with open(positions_path, "r") as f:
positions = json.load(f)
# Detect duplicate positions at the start
start_remove = 0
if positions:
first = positions[0]
for pos in positions:
if pos['latitude'] == first['latitude'] and pos['longitude'] == first['longitude']:
start_remove += 1
else:
break
if start_remove > 0:
start_remove -= 1
# Detect duplicate positions at the end
end_remove = 0
if positions:
last = positions[-1]
for pos in reversed(positions):
if pos['latitude'] == last['latitude'] and pos['longitude'] == last['longitude']:
end_remove += 1
else:
break
if end_remove > 0:
end_remove -= 1
# Shorten the positions list
new_positions = positions[start_remove:len(positions)-end_remove if end_remove > 0 else None]
# --- PAUSE DETECTION ---
pauses = []
if new_positions:
pause_start = None
pause_end = None
pause_location = None
for i in range(1, len(new_positions)):
prev = new_positions[i-1]
curr = new_positions[i]
# Check if stopped (same location)
if curr['latitude'] == prev['latitude'] and curr['longitude'] == prev['longitude']:
if pause_start is None:
pause_start = prev['deviceTime']
pause_location = (prev['latitude'], prev['longitude'])
pause_end = curr['deviceTime']
else:
if pause_start and pause_end:
# Calculate pause duration
t1 = datetime.datetime.fromisoformat(pause_start.replace('Z', '+00:00'))
t2 = datetime.datetime.fromisoformat(pause_end.replace('Z', '+00:00'))
duration = (t2 - t1).total_seconds()
if duration >= 120:
pauses.append({
"start_time": pause_start,
"end_time": pause_end,
"duration_seconds": int(duration),
"location": {"latitude": pause_location[0], "longitude": pause_location[1]}
})
pause_start = None
pause_end = None
pause_location = None
# Check for pause at the end
if pause_start and pause_end:
t1 = datetime.datetime.fromisoformat(pause_start.replace('Z', '+00:00'))
t2 = datetime.datetime.fromisoformat(pause_end.replace('Z', '+00:00'))
duration = (t2 - t1).total_seconds()
if duration >= 120:
pauses.append({
"start_time": pause_start,
"end_time": pause_end,
"duration_seconds": int(duration),
"location": {"latitude": pause_location[0], "longitude": pause_location[1]}
})
# --- FILTER PAUSES ---
# 1. Remove pauses near start/end
filtered_pauses = []
if new_positions and pauses:
start_lat, start_lon = new_positions[0]['latitude'], new_positions[0]['longitude']
end_lat, end_lon = new_positions[-1]['latitude'], new_positions[-1]['longitude']
for pause in pauses:
plat = pause["location"]["latitude"]
plon = pause["location"]["longitude"]
dist_start = haversine(start_lat, start_lon, plat, plon)
dist_end = haversine(end_lat, end_lon, plat, plon)
if dist_start < 50 or dist_end < 50:
continue # Skip pauses near start or end
filtered_pauses.append(pause)
else:
filtered_pauses = pauses
# 2. Merge pauses close in time and space
merged_pauses = []
filtered_pauses.sort(key=lambda p: p["start_time"])
for pause in filtered_pauses:
if not merged_pauses:
merged_pauses.append(pause)
else:
last = merged_pauses[-1]
# Time difference in seconds
t1 = datetime.datetime.fromisoformat(last["end_time"].replace('Z', '+00:00'))
t2 = datetime.datetime.fromisoformat(pause["start_time"].replace('Z', '+00:00'))
time_diff = (t2 - t1).total_seconds()
# Distance in meters
last_lat = last["location"]["latitude"]
last_lon = last["location"]["longitude"]
plat = pause["location"]["latitude"]
plon = pause["location"]["longitude"]
dist = haversine(last_lat, last_lon, plat, plon)
if time_diff < 300 and dist < 50:
# Merge: extend last pause's end_time and duration
last["end_time"] = pause["end_time"]
last["duration_seconds"] += pause["duration_seconds"]
else:
merged_pauses.append(pause)
pauses = merged_pauses
progress.value = 100
label.text = (
f"Entries removable at start: {start_remove}\n"
f"Entries removable at end: {end_remove}\n"
f"Detected pauses: {len(pauses)}"
)
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
btn_save = Button(text="Save optimized file", background_color=(0.008, 0.525, 0.290, 1))
btn_cancel = Button(text="Cancel")
btn_box = BoxLayout(orientation='horizontal', spacing=10, size_hint_y=None, height=44)
btn_box.add_widget(btn_save)
btn_box.add_widget(btn_cancel)
popup.content.add_widget(btn_box)
def save_optimized(instance):
with open(positions_path, "w") as f:
json.dump(new_positions, f, indent=2)
with open(pauses_path, "w") as f:
json.dump(pauses, f, indent=2)
label.text = "File optimized and pauses saved!"
btn_save.disabled = True
btn_cancel.disabled = True
def close_and_refresh(dt):
popup.dismiss()
if on_save:
on_save()
Clock.schedule_once(close_and_refresh, 1)
btn_save.bind(on_press=save_optimized)
btn_cancel.bind(on_press=lambda x: popup.dismiss())
Clock.schedule_once(process_entries, 0.5)