gps_system/app/Livewire/ReportsAndHistory.php
sackey 6b878bb0a0
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
Initial commit
2025-09-12 16:19:56 +00:00

407 lines
14 KiB
PHP

<?php
namespace App\Livewire;
use Livewire\Component;
use App\Models\Device;
use App\Models\Position;
use App\Models\Event;
use App\Services\TraccarService;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class ReportsAndHistory extends Component
{
public $selectedDevices = [];
public $startDate = '';
public $endDate = '';
public $reportType = 'route';
public $showMap = true;
public $includeMap = true;
public $exportFormat = 'pdf';
// Report data
public $reportData = [];
public $routeData = [];
public $summaryStats = [];
public $eventsData = [];
public $stopsData = [];
// Report settings
public $includeStops = true;
public $minStopDuration = 5; // minutes
public $maxSpeed = 120; // km/h for overspeed detection
public $groupByDate = false;
protected $traccarService;
public function boot(TraccarService $traccarService)
{
$this->traccarService = $traccarService;
}
public function mount()
{
// Set default date range (last 7 days)
$this->endDate = now()->format('Y-m-d');
$this->startDate = now()->subDays(7)->format('Y-m-d');
// Select all user devices by default
$deviceQuery = Device::where('user_id', Auth::id());
// Check if device groups have users relationship (safely handle missing relationship)
if (method_exists(\App\Models\DeviceGroup::class, 'users')) {
$deviceQuery->orWhereHas('group.users', function($subQuery) {
$subQuery->where('user_id', Auth::id());
});
}
$this->selectedDevices = $deviceQuery->pluck('id')->toArray();
}
public function generateReport()
{
$this->validate([
'selectedDevices' => 'required|array|min:1',
'startDate' => 'required|date',
'endDate' => 'required|date|after_or_equal:startDate',
]);
try {
switch ($this->reportType) {
case 'route':
$this->generateRouteReport();
break;
case 'summary':
$this->generateSummaryReport();
break;
case 'events':
$this->generateEventsReport();
break;
case 'stops':
$this->generateStopsReport();
break;
case 'trips':
$this->generateTripsReport();
break;
}
session()->flash('success', 'Report generated successfully!');
} catch (\Exception $e) {
session()->flash('error', 'Failed to generate report: ' . $e->getMessage());
}
}
private function generateRouteReport()
{
$startDateTime = Carbon::parse($this->startDate)->startOfDay();
$endDateTime = Carbon::parse($this->endDate)->endOfDay();
$this->routeData = [];
foreach ($this->selectedDevices as $deviceId) {
$device = Device::find($deviceId);
if (!$device) continue;
$positions = Position::where('device_id', $deviceId)
->whereBetween('device_time', [$startDateTime, $endDateTime])
->orderBy('device_time')
->get();
if ($positions->count() > 0) {
$route = [
'device' => $device,
'positions' => $positions,
'start_time' => $positions->first()->device_time,
'end_time' => $positions->last()->device_time,
'total_distance' => $this->calculateTotalDistance($positions),
'max_speed' => $positions->max('speed') ?? 0,
'avg_speed' => $positions->avg('speed') ?? 0,
];
if ($this->includeStops) {
$route['stops'] = $this->detectStops($positions);
}
$this->routeData[] = $route;
}
}
}
private function generateSummaryReport()
{
$startDateTime = Carbon::parse($this->startDate)->startOfDay();
$endDateTime = Carbon::parse($this->endDate)->endOfDay();
$this->summaryStats = [];
foreach ($this->selectedDevices as $deviceId) {
$device = Device::find($deviceId);
if (!$device) continue;
$positions = Position::where('device_id', $deviceId)
->whereBetween('device_time', [$startDateTime, $endDateTime])
->get();
$events = Event::where('device_id', $deviceId)
->whereBetween('event_time', [$startDateTime, $endDateTime])
->get();
$totalDistance = $this->calculateTotalDistance($positions);
$totalTime = $positions->count() > 1 ?
$positions->last()->device_time->diffInMinutes($positions->first()->device_time) : 0;
$this->summaryStats[] = [
'device' => $device,
'total_distance' => $totalDistance,
'total_time' => $totalTime,
'avg_speed' => $totalTime > 0 ? ($totalDistance / ($totalTime / 60)) : 0,
'max_speed' => $positions->max('speed') ?? 0,
'total_events' => $events->count(),
'overspeed_events' => $events->where('type', 'deviceOverspeed')->count(),
'online_time' => $this->calculateOnlineTime($positions),
'fuel_consumption' => $this->calculateFuelConsumption($positions),
];
}
}
private function generateEventsReport()
{
$startDateTime = Carbon::parse($this->startDate)->startOfDay();
$endDateTime = Carbon::parse($this->endDate)->endOfDay();
$this->eventsData = Event::with(['device', 'geofence'])
->whereIn('device_id', $this->selectedDevices)
->whereBetween('event_time', [$startDateTime, $endDateTime])
->orderBy('event_time', 'desc')
->get()
->groupBy(function($event) {
return $this->groupByDate ?
$event->event_time->format('Y-m-d') :
$event->device->name;
});
}
private function generateStopsReport()
{
$startDateTime = Carbon::parse($this->startDate)->startOfDay();
$endDateTime = Carbon::parse($this->endDate)->endOfDay();
$this->stopsData = [];
foreach ($this->selectedDevices as $deviceId) {
$device = Device::find($deviceId);
if (!$device) continue;
$positions = Position::where('device_id', $deviceId)
->whereBetween('device_time', [$startDateTime, $endDateTime])
->orderBy('device_time')
->get();
$stops = $this->detectStops($positions);
if (!empty($stops)) {
$this->stopsData[] = [
'device' => $device,
'stops' => $stops,
'total_stops' => count($stops),
'total_stop_time' => array_sum(array_column($stops, 'duration')),
];
}
}
}
private function generateTripsReport()
{
$startDateTime = Carbon::parse($this->startDate)->startOfDay();
$endDateTime = Carbon::parse($this->endDate)->endOfDay();
$this->reportData = [];
foreach ($this->selectedDevices as $deviceId) {
$device = Device::find($deviceId);
if (!$device) continue;
$positions = Position::where('device_id', $deviceId)
->whereBetween('device_time', [$startDateTime, $endDateTime])
->orderBy('device_time')
->get();
$trips = $this->detectTrips($positions);
$this->reportData[] = [
'device' => $device,
'trips' => $trips,
'total_trips' => count($trips),
'total_distance' => array_sum(array_column($trips, 'distance')),
'total_duration' => array_sum(array_column($trips, 'duration')),
];
}
}
private function calculateTotalDistance($positions)
{
$totalDistance = 0;
$previousPosition = null;
foreach ($positions as $position) {
if ($previousPosition) {
$distance = $this->calculateDistance(
$previousPosition->latitude,
$previousPosition->longitude,
$position->latitude,
$position->longitude
);
$totalDistance += $distance;
}
$previousPosition = $position;
}
return round($totalDistance, 2);
}
private function calculateDistance($lat1, $lon1, $lat2, $lon2)
{
$earthRadius = 6371; // kilometers
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat/2) * sin($dLat/2) +
cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
sin($dLon/2) * sin($dLon/2);
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
return $earthRadius * $c;
}
private function detectStops($positions)
{
$stops = [];
$currentStop = null;
$speedThreshold = 2; // km/h
foreach ($positions as $position) {
$speed = $position->speed ?? 0;
if ($speed <= $speedThreshold) {
if (!$currentStop) {
$currentStop = [
'start_time' => $position->device_time,
'latitude' => $position->latitude,
'longitude' => $position->longitude,
'address' => $position->address,
];
}
} else {
if ($currentStop) {
$currentStop['end_time'] = $position->device_time;
$currentStop['duration'] = $currentStop['start_time']->diffInMinutes($currentStop['end_time']);
if ($currentStop['duration'] >= $this->minStopDuration) {
$stops[] = $currentStop;
}
$currentStop = null;
}
}
}
return $stops;
}
private function detectTrips($positions)
{
$trips = [];
$currentTrip = null;
$speedThreshold = 2; // km/h
foreach ($positions as $position) {
$speed = $position->speed ?? 0;
if ($speed > $speedThreshold) {
if (!$currentTrip) {
$currentTrip = [
'start_time' => $position->device_time,
'start_latitude' => $position->latitude,
'start_longitude' => $position->longitude,
'start_address' => $position->address,
'positions' => [$position],
];
} else {
$currentTrip['positions'][] = $position;
}
} else {
if ($currentTrip && count($currentTrip['positions']) > 1) {
$lastPosition = end($currentTrip['positions']);
$currentTrip['end_time'] = $lastPosition->device_time;
$currentTrip['end_latitude'] = $lastPosition->latitude;
$currentTrip['end_longitude'] = $lastPosition->longitude;
$currentTrip['end_address'] = $lastPosition->address;
$currentTrip['duration'] = $currentTrip['start_time']->diffInMinutes($currentTrip['end_time']);
$currentTrip['distance'] = $this->calculateTotalDistance(collect($currentTrip['positions']));
$currentTrip['max_speed'] = collect($currentTrip['positions'])->max('speed') ?? 0;
unset($currentTrip['positions']); // Remove positions to save memory
$trips[] = $currentTrip;
$currentTrip = null;
}
}
}
return $trips;
}
private function calculateOnlineTime($positions)
{
if ($positions->count() < 2) return 0;
return $positions->first()->device_time->diffInMinutes($positions->last()->device_time);
}
private function calculateFuelConsumption($positions)
{
// This is a simplified calculation - in reality you'd use more sophisticated methods
$totalDistance = $this->calculateTotalDistance($positions);
$avgConsumption = 8; // liters per 100km (default)
return round(($totalDistance / 100) * $avgConsumption, 2);
}
public function exportReport()
{
// This would implement actual export functionality
session()->flash('success', 'Report export started. You will receive a download link shortly.');
}
public function render()
{
$deviceQuery = Device::where('user_id', Auth::id());
// Check if device groups have users relationship (safely handle missing relationship)
if (method_exists(\App\Models\DeviceGroup::class, 'users')) {
$deviceQuery->orWhereHas('group.users', function($subQuery) {
$subQuery->where('user_id', Auth::id());
});
}
$devices = $deviceQuery->get();
// Get recent events for the current user
$recentEvents = Event::with(['device', 'position'])
->whereHas('device', function($q) {
$q->where('user_id', Auth::id());
})
->orderBy('event_time', 'desc')
->limit(10)
->get();
return view('livewire.reports-and-history', [
'devices' => $devices,
'recentEvents' => $recentEvents,
]);
}
}