MotorSignal-App/lib/services/alerts/swarm_road_alert_models.dart

190 lines
6.0 KiB
Dart

import 'package:flutter/foundation.dart';
import 'road_alert_models.dart';
@immutable
class SwarmReport {
final String id;
final RoadAlertCategory category;
final String? subtype;
final double latitude;
final double longitude;
final double? heading;
final String? roadName;
final String description;
final String reporterUid;
final DateTime reportedAt;
final DateTime expiresAt;
final int confirmations;
final int dismissals;
final String? groupRideId;
final String? source;
final RoadAlertStatus status;
const SwarmReport({
required this.id,
required this.category,
this.subtype,
required this.latitude,
required this.longitude,
this.heading,
this.roadName,
required this.description,
required this.reporterUid,
required this.reportedAt,
required this.expiresAt,
this.confirmations = 1,
this.dismissals = 0,
this.groupRideId,
this.source,
this.status = RoadAlertStatus.active,
});
double get confidence {
final total = confirmations + dismissals;
if (total == 0) return 0.8;
return (confirmations / total).clamp(0.1, 1.0);
}
bool get isExpired => DateTime.now().isAfter(expiresAt);
bool get isCommunityConfirmed => confirmations >= 2;
Map<String, dynamic> toJson() => {
'id': id,
'category': category.name,
if (subtype != null) 'subtype': subtype,
'latitude': latitude,
'longitude': longitude,
if (heading != null) 'heading': heading,
if (roadName != null) 'roadName': roadName,
'description': description,
'reporterUid': reporterUid,
'reportedAt': reportedAt.millisecondsSinceEpoch,
'expiresAt': expiresAt.millisecondsSinceEpoch,
'confirmations': confirmations,
'dismissals': dismissals,
if (groupRideId != null) 'groupRideId': groupRideId,
if (source != null) 'source': source,
'status': status.name,
};
factory SwarmReport.fromJson(Map<String, dynamic> json) {
final catName = json['category']?.toString() ?? 'other';
final category = RoadAlertCategory.values.firstWhere(
(c) => c.name == catName,
orElse: () => RoadAlertCategory.other,
);
final rawReportedAt = json['reportedAt'];
final reportedAt = rawReportedAt is num
? DateTime.fromMillisecondsSinceEpoch(rawReportedAt.toInt())
: (DateTime.tryParse(rawReportedAt?.toString() ?? '') ?? DateTime.now());
final rawExpiresAt = json['expiresAt'];
final expiresAt = rawExpiresAt is num
? DateTime.fromMillisecondsSinceEpoch(rawExpiresAt.toInt())
: (DateTime.tryParse(rawExpiresAt?.toString() ?? '') ??
reportedAt.add(const Duration(hours: 1)));
final confirmations = (json['confirmations'] as num?)?.toInt() ?? 1;
final dismissals = (json['dismissals'] as num?)?.toInt() ?? 0;
final rawStatus = json['status']?.toString();
final status = RoadAlertStatus.values.firstWhere(
(s) => s.name == rawStatus,
orElse: () => confirmations >= 2
? RoadAlertStatus.confirmed
: RoadAlertStatus.active,
);
return SwarmReport(
id: json['id']?.toString() ?? '',
category: category,
subtype: json['subtype']?.toString(),
latitude: (json['latitude'] as num).toDouble(),
longitude: (json['longitude'] as num).toDouble(),
heading: json['heading'] != null ? (json['heading'] as num).toDouble() : null,
roadName: json['roadName']?.toString(),
description: json['description']?.toString() ?? 'Swarm Report',
reporterUid: json['reporterUid']?.toString() ?? 'anonymous',
reportedAt: reportedAt,
expiresAt: expiresAt,
confirmations: confirmations,
dismissals: dismissals,
groupRideId: json['groupRideId']?.toString(),
source: json['source']?.toString(),
status: status,
);
}
RoadAlert toRoadAlert({double? distanceFromUser}) {
final isHighSeverity = category == RoadAlertCategory.police ||
category == RoadAlertCategory.accident ||
category == RoadAlertCategory.weatherHazard;
final sourceName = source ??
(groupRideId != null ? 'Pack Rider Report' : 'MotorSignal Swarm');
return RoadAlert(
id: id,
providerId: 'swarm_alerts',
category: category,
subtype: subtype,
latitude: latitude,
longitude: longitude,
heading: heading,
roadName: roadName,
description: description,
reportedAt: reportedAt,
expiresAt: expiresAt,
lastUpdated: DateTime.now(),
confidence: confidence,
severity: isHighSeverity ? RoadAlertSeverity.high : RoadAlertSeverity.medium,
source: sourceName,
sourceType: RoadAlertSourceType.userReported,
status: status,
confirmations: confirmations,
dismissals: dismissals,
reporterUid: reporterUid,
distanceFromUser: distanceFromUser,
);
}
SwarmReport copyWith({
String? id,
RoadAlertCategory? category,
String? subtype,
double? latitude,
double? longitude,
double? heading,
String? roadName,
String? description,
String? reporterUid,
DateTime? reportedAt,
DateTime? expiresAt,
int? confirmations,
int? dismissals,
String? groupRideId,
String? source,
RoadAlertStatus? status,
}) {
return SwarmReport(
id: id ?? this.id,
category: category ?? this.category,
subtype: subtype ?? this.subtype,
latitude: latitude ?? this.latitude,
longitude: longitude ?? this.longitude,
heading: heading ?? this.heading,
roadName: roadName ?? this.roadName,
description: description ?? this.description,
reporterUid: reporterUid ?? this.reporterUid,
reportedAt: reportedAt ?? this.reportedAt,
expiresAt: expiresAt ?? this.expiresAt,
confirmations: confirmations ?? this.confirmations,
dismissals: dismissals ?? this.dismissals,
groupRideId: groupRideId ?? this.groupRideId,
source: source ?? this.source,
status: status ?? this.status,
);
}
}