updated app

This commit is contained in:
2025-07-07 12:20:16 +03:00
parent c28be4e083
commit a38e2b1fe9
35 changed files with 12 additions and 545606 deletions

View File

@@ -5,7 +5,7 @@ import os
import json
from kivy.clock import Clock
from kivy.properties import StringProperty, ListProperty
from utils import (
from py_scripts.utils import (
generate_key, load_key, encrypt_data, decrypt_data,
check_server_settings, save_server_settings,
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day

2
py_scripts/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
# py_scripts package
# Contains utility scripts for the Traccar Animation application

Binary file not shown.

Binary file not shown.

View File

@@ -219,7 +219,8 @@ def process_preview_util(
import folium
import os
import json
from utils import html_to_image
# Import html_to_image function from within the same module
# (it's defined later in this file)
try:
project_folder = os.path.join(RESOURCES_FOLDER, "projects", project_name)

View File

@@ -1 +1 @@
gAAAAABoZS4Ed-JJHBU90HAwhBvMJ7HfCSU-AeSaFDEt-Hg1wJW57NBoZJw_9lXEw-uooZCbQnMEUAd9mmF_I_FR1oRC-u_6cqU01jHGQq8OzJubmvBiZ1zYVlCO-kpmmFB8jv0LaBLU
gAAAAABoa47dY_7ed4KPuQv7x1BWyfC8-MEwtoIo0u5lhW2Qp1BwdtL9Biry5xG0BhOGE7MgaO7-kSKJuZDiOxVzSXenDEeT0Bq7dW5GvIK8o_7Z5CN0gyXog_bBCV3FZvQ-b_s9fCkn

Binary file not shown.

Before

Width:  |  Height:  |  Size: 537 KiB

View File

@@ -1,232 +0,0 @@
[
{
"start_time": "2025-06-15T06:00:23.000+00:00",
"end_time": "2025-06-15T06:15:19.000+00:00",
"duration_seconds": 897,
"location": {
"latitude": 45.78012944444444,
"longitude": 24.16865166666667
},
"location_suggestion": "\u0218oseaua Sibiului"
},
{
"start_time": "2025-06-15T07:27:39.000+00:00",
"end_time": "2025-06-15T07:56:41.000+00:00",
"duration_seconds": 1744,
"location": {
"latitude": 45.35275055555555,
"longitude": 24.042727222222222
},
"location_suggestion": "Strada Podul \u0218ipotului 1"
},
{
"start_time": "2025-06-15T08:36:25.000+00:00",
"end_time": "2025-06-15T08:48:10.000+00:00",
"duration_seconds": 702,
"location": {
"latitude": 45.390987777777774,
"longitude": 23.980355
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T09:02:08.000+00:00",
"end_time": "2025-06-15T09:09:53.765+00:00",
"duration_seconds": 466,
"location": {
"latitude": 45.409075,
"longitude": 23.938845
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T10:29:06.000+00:00",
"end_time": "2025-06-15T11:04:51.000+00:00",
"duration_seconds": 2180,
"location": {
"latitude": 45.40981166666667,
"longitude": 23.793883888888892
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T11:21:38.000+00:00",
"end_time": "2025-06-15T11:24:05.928+00:00",
"duration_seconds": 147,
"location": {
"latitude": 45.38780444444444,
"longitude": 23.751438888888888
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T11:25:30.000+00:00",
"end_time": "2025-06-15T11:28:36.000+00:00",
"duration_seconds": 186,
"location": {
"latitude": 45.390328333333336,
"longitude": 23.74829666666667
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T11:42:42.000+00:00",
"end_time": "2025-06-15T11:51:12.000+00:00",
"duration_seconds": 501,
"location": {
"latitude": 45.39019166666667,
"longitude": 23.70160277777778
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T11:52:48.000+00:00",
"end_time": "2025-06-15T12:01:47.000+00:00",
"duration_seconds": 540,
"location": {
"latitude": 45.39456,
"longitude": 23.69228777777778
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T12:12:38.000+00:00",
"end_time": "2025-06-15T12:19:57.845+00:00",
"duration_seconds": 440,
"location": {
"latitude": 45.38043944444445,
"longitude": 23.651888333333336
},
"location_suggestion": "Strategica"
},
{
"start_time": "2025-06-15T12:58:38.000+00:00",
"end_time": "2025-06-15T13:53:36.000+00:00",
"duration_seconds": 3297,
"location": {
"latitude": 45.57911,
"longitude": 23.62189888888889
},
"location_suggestion": "Transalpina"
},
{
"start_time": "2025-06-15T14:28:22.000+00:00",
"end_time": "2025-06-15T14:38:39.000+00:00",
"duration_seconds": 618,
"location": {
"latitude": 45.78371777777778,
"longitude": 23.613761666666665
},
"location_suggestion": "Strada Valea Frumoasei"
},
{
"start_time": "2025-06-19T05:25:20.000+00:00",
"end_time": "2025-06-19T14:09:05.000+00:00",
"duration_seconds": 31426,
"location": {
"latitude": 45.79909222222222,
"longitude": 24.085676666666668
},
"location_suggestion": "Strada Monaco"
},
{
"start_time": "2025-06-20T05:18:03.000+00:00",
"end_time": "2025-06-20T14:05:26.784+00:00",
"duration_seconds": 31644,
"location": {
"latitude": 45.798976111111116,
"longitude": 24.085469444444445
},
"location_suggestion": "Strada Monaco"
},
{
"start_time": "2025-06-21T07:45:07.000+00:00",
"end_time": "2025-06-21T08:04:04.000+00:00",
"duration_seconds": 1049,
"location": {
"latitude": 45.82062444444445,
"longitude": 24.151072222222222
},
"location_suggestion": "Calea \u0218urii Mari 36"
},
{
"start_time": "2025-06-23T05:16:09.000+00:00",
"end_time": "2025-06-23T05:18:52.000+00:00",
"duration_seconds": 163,
"location": {
"latitude": 45.79242277777778,
"longitude": 24.139758888888892
},
"location_suggestion": "\u0218oseaua Alba Iulia"
},
{
"start_time": "2025-06-23T05:26:59.000+00:00",
"end_time": "2025-06-23T13:42:32.000+00:00",
"duration_seconds": 29558,
"location": {
"latitude": 45.79897166666667,
"longitude": 24.085509444444444
},
"location_suggestion": "Strada Monaco"
},
{
"start_time": "2025-06-24T05:02:36.000+00:00",
"end_time": "2025-06-24T05:06:24.161+00:00",
"duration_seconds": 228,
"location": {
"latitude": 45.79917111111111,
"longitude": 24.086246666666668
},
"location_suggestion": "Strada Europa Unit\u0103"
},
{
"start_time": "2025-06-24T05:06:26.000+00:00",
"end_time": "2025-06-24T13:44:34.000+00:00",
"duration_seconds": 31088,
"location": {
"latitude": 45.79897666666667,
"longitude": 24.08552166666667
},
"location_suggestion": "Strada Monaco"
},
{
"start_time": "2025-06-25T05:23:38.000+00:00",
"end_time": "2025-06-25T13:52:00.000+00:00",
"duration_seconds": 30500,
"location": {
"latitude": 45.799183888888884,
"longitude": 24.085637777777777
},
"location_suggestion": "Strada Monaco"
},
{
"start_time": "2025-06-26T05:10:24.000+00:00",
"end_time": "2025-06-26T14:09:48.774+00:00",
"duration_seconds": 32365,
"location": {
"latitude": 45.799045,
"longitude": 24.085640555555557
},
"location_suggestion": "Strada Monaco"
},
{
"start_time": "2025-06-27T05:13:04.000+00:00",
"end_time": "2025-06-27T14:08:01.630+00:00",
"duration_seconds": 32098,
"location": {
"latitude": 45.79897444444445,
"longitude": 24.085511666666665
},
"location_suggestion": "Strada Monaco"
},
{
"start_time": "2025-06-30T04:39:06.000+00:00",
"end_time": "2025-06-30T13:13:43.000+00:00",
"duration_seconds": 30878,
"location": {
"latitude": 45.798997222222226,
"longitude": 24.085517777777778
},
"location_suggestion": "Strada Monaco"
}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

View File

@@ -1,9 +0,0 @@
To update the colors to the specified values, we will convert the hex color codes to RGBA format (values between 0 and 1) and update the `server_box_color` property in the `HomeScreen` class.
Here are the RGBA equivalents of the provided hex colors:
- **Yellow (#FB8D14)**: `(0.984, 0.553, 0.078, 1)`
- **Red (#E8083E)**: `(0.909, 0.031, 0.243, 1)`
- **Green (#02864A)**: `(0.008, 0.525, 0.290, 1)`
The RGBA equivalent of `#573CFA` is `(0.341, 0.235, 0.980, 1)`.

View File

@@ -1,3 +0,0 @@
python -m venv track
source track/bin/activate
pip install -r reqirements.txt

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +0,0 @@
import requests
def get_device_route(server_url, token, device_id, from_time, to_time):
"""
Fetch all positions for a device in a time frame from Traccar server.
"""
url = f"{server_url}/reports/route"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json"
}
params = {
"deviceId": device_id,
"from": from_time,
"to": to_time
}
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
try:
positions = response.json()
print(f"Retrieved {len(positions)} positions.")
return positions
except Exception as e:
print(f"Error parsing JSON: {e}")
print(response.text)
return []
else:
print(f"Failed to fetch positions: {response.status_code} - {response.text}")
return []
# Example usage:
if __name__ == "__main__":
# Use your actual Traccar API endpoint (not /reports/route)
server_url = "https://gps.moto-adv.com/api"
token = "SDBGAiEA4sNXvVhL8w_Jd-5oCiXAuS5La5yelCQemfNysZYItaMCIQDOkzaoWKHbNnbZJw9ruGlsvbp35d90x3EGOZLXW_Gls3sidSI6MSwiZSI6IjIwMjUtMDYtMDlUMjE6MDA6MDAuMDAwKzAwOjAwIn0"
device_id = 1 # Replace with your device ID
from_time = "2024-06-02T21:00:00Z"
to_time = "2025-06-03T20:59:00Z"
positions = get_device_route(server_url, token, device_id, from_time, to_time)
for pos in positions:
print(f"{pos['deviceTime']}: {pos['latitude']}, {pos['longitude']}")

View File

@@ -1 +0,0 @@
SDBGAiEA4sNXvVhL8w_Jd-5oCiXAuS5La5yelCQemfNysZYItaMCIQDOkzaoWKHbNnbZJw9ruGlsvbp35d90x3EGOZLXW_Gls3sidSI6MSwiZSI6IjIwMjUtMDYtMDlUMjE6MDA6MDAuMDAwKzAwOjAwIn0

View File

@@ -5,10 +5,10 @@ import json
import math
from kivy.clock import Clock
from kivy.properties import StringProperty, NumericProperty, AliasProperty
from utils import (
from py_scripts.utils import (
process_preview_util, optimize_route_entries_util
)
from video_3d_generator import generate_3d_video_animation
from py_scripts.video_3d_generator import generate_3d_video_animation
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.label import Label

View File

@@ -5,7 +5,7 @@ import os
import json
from kivy.clock import Clock
from kivy.properties import StringProperty, ListProperty
from utils import (
from py_scripts.utils import (
generate_key, load_key, encrypt_data, decrypt_data,
check_server_settings, save_server_settings,
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day

View File

@@ -9,7 +9,7 @@ from kivy.clock import Clock
import os
import json
from config import RESOURCES_FOLDER, CREDENTIALS_FILE
from utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
from py_scripts.utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
class IconButton(ButtonBehavior, Image):

View File

@@ -9,7 +9,7 @@ from kivy.clock import Clock
import os
import json
from config import RESOURCES_FOLDER, CREDENTIALS_FILE
from utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
from py_scripts.utils import encrypt_data, decrypt_data, check_server_settings, save_server_settings, test_connection
class LoginScreen(Screen):

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ import os
import json
from kivy.clock import Clock
from kivy.properties import StringProperty, ListProperty
from utils import (
from py_scripts.utils import (
generate_key, load_key, encrypt_data, decrypt_data,
check_server_settings, save_server_settings,
test_connection, get_devices_from_server, save_route_to_file, fetch_positions_for_selected_day

View File

@@ -1,284 +0,0 @@
from kivy.uix.popup import Popup
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.filechooser import FileChooserIconView
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle, Line
from kivy.uix.scrollview import ScrollView
import os
import json
import shutil
from geopy.geocoders import Nominatim
def open_pauses_popup(screen_instance, project_name, RESOURCES_FOLDER, on_save_callback=None):
project_folder = os.path.join(RESOURCES_FOLDER, "projects", project_name)
pauses_path = os.path.join(project_folder, "pauses.json")
# Main layout with dark background
layout = BoxLayout(orientation='vertical', spacing=14, padding=14)
with layout.canvas.before:
Color(0.13, 0.13, 0.16, 1)
layout.bg_rect = Rectangle(pos=layout.pos, size=layout.size)
def update_bg_rect(instance, value):
layout.bg_rect.pos = layout.pos
layout.bg_rect.size = layout.size
layout.bind(pos=update_bg_rect, size=update_bg_rect)
pauses = []
if os.path.exists(pauses_path):
with open(pauses_path, "r") as f:
pauses = json.load(f)
def suggest_location_name(lat, lon):
try:
geolocator = Nominatim(user_agent="traccar_animation")
location = geolocator.reverse((lat, lon), exactly_one=True, radius=50, timeout=10)
if not location or not location.raw or not location.address:
return f"{lat:.5f}, {lon:.5f}"
address = location.raw.get('address', {})
# 1. Place or location nearby (amenity, attraction, building, etc.)
for key in ['attraction', 'building', 'amenity', 'shop', 'leisure', 'tourism', 'place', 'public_building']:
if key in address:
return address[key]
# 2. Street name and number
if 'road' in address and 'house_number' in address:
return f"{address['road']} {address['house_number']}"
# 3. Road name
if 'road' in address:
return address['road']
# 4. Fallback: lat/lon
return f"{lat:.5f}, {lon:.5f}"
except Exception:
return f"{lat:.5f}, {lon:.5f}"
# Main layout for popup (vertical)
layout = BoxLayout(orientation='vertical', spacing=14, padding=14)
# Scrollable area for pauses
pauses_layout = BoxLayout(orientation='vertical', spacing=14, size_hint_y=None, padding=0)
pauses_layout.bind(minimum_height=pauses_layout.setter('height'))
for idx, pause in enumerate(pauses):
# Main vertical box for this pause
pause_box = BoxLayout(
orientation='vertical',
spacing=6,
padding=[6, 0, 6, 4],
size_hint_y=None,
height=170
)
with pause_box.canvas.before:
Color(0.20, 0.20, 0.25, 1)
pause_box.bg_rect = Rectangle(pos=pause_box.pos, size=pause_box.size)
Color(0.3, 0.5, 0.9, 1) # Border color
pause_box.border_line = Line(rectangle=(pause_box.x, pause_box.y, pause_box.width, pause_box.height), width=1.2)
def update_pause_box(instance, value, box=pause_box):
box.bg_rect.pos = box.pos
box.bg_rect.size = box.size
box.border_line.rectangle = (box.x, box.y, box.width, box.height)
pause_box.bind(pos=update_pause_box, size=update_pause_box)
# --- Row 1: Name label ---
title_label = Label(
text=f"[b]Pause {idx+1}[/b]",
markup=True,
font_size=15,
color=(1, 1, 1, 1),
size_hint_y=0.2, # 20% of the pause_box height
halign="center",
valign="middle"
)
def update_title_size(instance, value):
instance.text_size = (instance.width, None)
title_label.bind(width=update_title_size)
pause_box.add_widget(title_label)
# --- Row 2: Location group ---
place_group = BoxLayout(orientation='vertical', spacing=4, padding=4, size_hint_y=0.4)
with place_group.canvas.before:
Color(0.3, 0.5, 0.9, 0.3)
place_group.border_rect = Rectangle(pos=place_group.pos, size=place_group.size)
Color(0.3, 0.5, 0.9, 1)
place_group.border_line = Line(rectangle=(place_group.x, place_group.y, place_group.width, place_group.height), width=1)
def update_place_group(instance, value, box=place_group):
box.border_rect.pos = box.pos
box.border_rect.size = box.size
box.border_line.rectangle = (box.x, box.y, box.width, box.height)
place_group.bind(pos=update_place_group, size=update_place_group)
suggested_place = suggest_location_name(pause["location"]["latitude"], pause["location"]["longitude"])
suggested_label = Label(
text=f"Suggested place: {suggested_place}",
font_size=9,
color=(0.7, 0.9, 1, 1),
size_hint_y=None,
height=12,
halign="left",
valign="middle"
)
suggested_label.bind(width=update_title_size)
place_group.add_widget(suggested_label)
personalized_label = Label(
text=f"Personalized name place: {pause.get('name', '')}",
font_size=9,
color=(1, 1, 1, 1),
size_hint_y=None,
height=12,
halign="left",
valign="middle"
)
personalized_label.bind(width=update_title_size)
edit_loc_btn = Button(
text="Edit Place",
size_hint_x=None,
width=70,
font_size=10,
background_color=(0.341, 0.235, 0.980, 1),
color=(1, 1, 1, 1),
height=16
)
def open_edit_popup(instance, pause=pause, personalized_label=personalized_label):
edit_layout = BoxLayout(orientation='vertical', spacing=4, padding=4)
with edit_layout.canvas.before:
Color(0.13, 0.13, 0.16, 1)
edit_layout.bg_rect = Rectangle(pos=edit_layout.pos, size=edit_layout.size)
edit_layout.bind(pos=lambda inst, val: setattr(edit_layout.bg_rect, 'pos', inst.pos),
size=lambda inst, val: setattr(edit_layout.bg_rect, 'size', inst.size))
input_field = TextInput(text=pause.get('name', ''), multiline=False, background_color=(0.18,0.18,0.22,1), foreground_color=(1,1,1,1), font_size=10, height=18)
save_btn = Button(text="Save", background_color=(0.341, 0.235, 0.980, 1), color=(1,1,1,1), height=18, font_size=10)
edit_layout.add_widget(Label(text="Edit Place Name:", color=(1,1,1,1), font_size=10, height=12, size_hint_y=None))
edit_layout.add_widget(input_field)
edit_layout.add_widget(save_btn)
edit_popup = Popup(title="Edit Place", content=edit_layout, size_hint=(0.7, None), size=(0, 80))
def save_name(instance):
pause['name'] = input_field.text
personalized_label.text = f"Personalized name place: {input_field.text}"
edit_popup.dismiss()
save_btn.bind(on_press=save_name)
edit_popup.open()
edit_loc_btn.bind(on_press=open_edit_popup)
personalized_row = BoxLayout(orientation='horizontal', spacing=2, size_hint_y=None, height=14)
personalized_row.add_widget(personalized_label)
personalized_row.add_widget(edit_loc_btn)
place_group.add_widget(personalized_row)
pause_box.add_widget(place_group)
# --- Row 3: Pictures group ---
photo_group = BoxLayout(orientation='vertical', spacing=1, padding=2, size_hint_y=0.4)
with photo_group.canvas.before:
Color(0.2, 0.8, 0.5, 0.2)
photo_group.border_rect = Rectangle(pos=photo_group.pos, size=photo_group.size)
Color(0.2, 0.8, 0.5, 1)
photo_group.border_line = Line(rectangle=(photo_group.x, photo_group.y, photo_group.width, photo_group.height), width=1)
def update_photo_group(instance, value, box=photo_group):
box.border_rect.pos = box.pos
box.border_rect.size = box.size
box.border_line.rectangle = (box.x, box.y, box.width, box.height)
photo_group.bind(pos=update_photo_group, size=update_photo_group)
pause_img_folder = os.path.join(project_folder, f"pause_{idx+1}")
os.makedirs(pause_img_folder, exist_ok=True)
img_list = [f for f in os.listdir(pause_img_folder) if os.path.isfile(os.path.join(pause_img_folder, f))]
if img_list:
photo_count_label = Label(
text=f"You have {len(img_list)} photo(s) for this pause.",
font_size=9,
color=(0.8, 0.8, 0.8, 1),
size_hint_y=None,
height=12
)
photo_list_label = Label(
text=", ".join(img_list),
font_size=8,
color=(0.7, 0.7, 0.7, 1),
size_hint_y=None,
height=10
)
else:
photo_count_label = Label(
text="No photos are set for this pause.",
font_size=9,
color=(0.8, 0.8, 0.8, 1),
size_hint_y=None,
height=12
)
photo_list_label = Label(
text="",
font_size=8,
color=(0.7, 0.7, 0.7, 1),
size_hint_y=None,
height=10
)
photo_group.add_widget(photo_count_label)
photo_group.add_widget(photo_list_label)
browse_btn = Button(
text="Browse",
size_hint_x=0.33,
size_hint_y=None,
height=16,
font_size=10,
background_color=(0.341, 0.235, 0.980, 1),
color=(1,1,1,1)
)
delete_btn = Button(
text="Delete",
size_hint_x=0.34,
size_hint_y=None,
height=16,
font_size=10,
background_color=(0.8, 0.1, 0.1, 1),
color=(1,1,1,1)
)
save_pause_btn = Button(
text="Save Pause",
size_hint_x=0.33,
size_hint_y=None,
height=16,
font_size=10,
background_color=(0.341, 0.235, 0.980, 1),
color=(1,1,1,1)
)
bottom_box = BoxLayout(orientation='horizontal', spacing=4, size_hint_y=None, height=18)
bottom_box.add_widget(browse_btn)
bottom_box.add_widget(delete_btn)
bottom_box.add_widget(save_pause_btn)
photo_group.add_widget(bottom_box)
pause_box.add_widget(photo_group)
# Separator line
sep = Widget(size_hint_y=None, height=1)
with sep.canvas:
Color(0.25, 0.25, 0.30, 1)
Line(points=[0, 1, 1000, 1], width=1)
pause_box.add_widget(sep)
pauses_layout.add_widget(pause_box)
scroll = ScrollView(size_hint=(1, 1))
scroll.add_widget(pauses_layout)
layout.add_widget(scroll)
# Save all and close
save_all_btn = Button(text="Save All & Close", size_hint_y=None, height=44, background_color=(0.341, 0.235, 0.980, 1), color=(1,1,1,1))
def save_all(instance):
with open(pauses_path, "w") as f:
json.dump(pauses, f, indent=2)
if on_save_callback:
on_save_callback()
popup.dismiss()
save_all_btn.bind(on_press=save_all)
layout.add_widget(save_all_btn)
popup = Popup(title="Edit Pauses", content=layout, size_hint=(0.95, 0.95), background_color=(0.13, 0.13, 0.16, 1))
popup.open()