252 lines
No EOL
6.5 KiB
Dart
252 lines
No EOL
6.5 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:json_annotation/json_annotation.dart';
|
|
import 'package:rxdart/subjects.dart';
|
|
|
|
part 'train_data.g.dart';
|
|
|
|
class TrainDataSourceWidget extends InheritedWidget {
|
|
final TrainDataSource dataSource;
|
|
|
|
@override
|
|
bool updateShouldNotify(InheritedWidget oldWidget) {
|
|
return true;
|
|
}
|
|
|
|
TrainDataSourceWidget({Widget child, Key key})
|
|
: dataSource = TrainDataSource(),
|
|
super(key: key, child: child);
|
|
|
|
static TrainDataSource of(BuildContext context) {
|
|
return (context.inheritFromWidgetOfExactType(TrainDataSourceWidget) as TrainDataSourceWidget).dataSource;
|
|
}
|
|
}
|
|
|
|
class TrainDataSource {
|
|
Future lookup(String trainNumber, [bool alternate = false]) async {
|
|
_controller.add(
|
|
TrainDataEvent(
|
|
trainNumber: trainNumber,
|
|
kind: TrainDataEventKind.LOADING
|
|
)
|
|
);
|
|
|
|
try {
|
|
final response = await http.get(
|
|
"http://cfr-scrapper.herokuapp.com/train/$trainNumber${alternate ? "?alt" : ""}", headers: {
|
|
"Authorization": "Flutter - Info Tren",
|
|
"X-Issued-By": "Flutter App"
|
|
}
|
|
);
|
|
|
|
if (200 <= response.statusCode && response.statusCode <= 299) {
|
|
// OK
|
|
final text = response.body;
|
|
final ser = json.decode(text
|
|
);
|
|
final data = TrainData.fromJson(ser
|
|
);
|
|
_controller.add(
|
|
TrainDataEvent(
|
|
kind: TrainDataEventKind.GOT_DATA,
|
|
trainNumber: trainNumber,
|
|
trainData: data
|
|
)
|
|
);
|
|
}
|
|
else if (response.statusCode == 404) {
|
|
_controller.addError(
|
|
TrainDataEvent(
|
|
kind: TrainDataEventKind.NOT_FOUND,
|
|
trainNumber: trainNumber,
|
|
errorStatusCode: 404
|
|
)
|
|
);
|
|
}
|
|
else if (response.statusCode == 503) {
|
|
_controller.addError(
|
|
TrainDataEvent(
|
|
kind: TrainDataEventKind.TIMEOUT,
|
|
trainNumber: trainNumber,
|
|
errorStatusCode: 503
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
// Throw error
|
|
_controller.addError(
|
|
TrainDataEvent(
|
|
kind: TrainDataEventKind.UNKNOWN_ERROR,
|
|
trainNumber: trainNumber,
|
|
errorStatusCode: response.statusCode
|
|
)
|
|
);
|
|
}
|
|
}
|
|
catch (e) {
|
|
_controller.addError(
|
|
TrainDataEvent(
|
|
kind: TrainDataEventKind.UNKNOWN_ERROR,
|
|
trainNumber: trainNumber,
|
|
errorStatusCode: -1
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
TrainDataSource() {
|
|
_controller = BehaviorSubject<TrainDataEvent>();
|
|
}
|
|
|
|
dispose() {
|
|
_controller.close();
|
|
}
|
|
|
|
BehaviorSubject<TrainDataEvent> _controller;
|
|
Stream<TrainDataEvent> get dataStream => _controller.stream;
|
|
}
|
|
|
|
enum TrainDataEventKind {
|
|
LOADING,
|
|
TIMEOUT,
|
|
NOT_FOUND,
|
|
UNKNOWN_ERROR,
|
|
GOT_DATA
|
|
}
|
|
|
|
class TrainDataEvent {
|
|
final TrainData trainData;
|
|
final TrainDataEventKind kind;
|
|
final String trainNumber;
|
|
final int errorStatusCode;
|
|
|
|
TrainDataEvent({this.kind, this.trainData, this.trainNumber, this.errorStatusCode});
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class TrainData {
|
|
final String rang;
|
|
@JsonKey(name: "tren")
|
|
final String trainNumber;
|
|
final String operator;
|
|
@JsonKey(name: "relatia")
|
|
final String route;
|
|
@JsonKey(name: "stare")
|
|
final String state;
|
|
@JsonKey(name: "ultima_informatie")
|
|
final LastInfo lastInfo;
|
|
@JsonKey(name: "destinatie")
|
|
final StopInfo destination;
|
|
@JsonKey(name: "urmatoarea_oprire")
|
|
final StopInfo nextStop;
|
|
@JsonKey(name: "durata_calatoriei")
|
|
final String tripLength;
|
|
@JsonKey(name: "distanta")
|
|
final String distance;
|
|
|
|
@JsonKey(name: "stations")
|
|
List<StationEntry> stations;
|
|
|
|
TrainData({this.rang, this.trainNumber, this.operator, this.lastInfo,
|
|
this.state, this.route, this.tripLength, this.stations, this.nextStop,
|
|
this.distance, this.destination});
|
|
factory TrainData.fromJson(Map<String, dynamic> json) {
|
|
var result = _$TrainDataFromJson(json);
|
|
var foundEstimat = false;
|
|
result.stations = result.stations.map((station) {
|
|
if (station.realOrEstimate == "Estimat") {
|
|
foundEstimat = true;
|
|
}
|
|
|
|
station.realOrEstimate = foundEstimat ? "Estimat" : "Real";
|
|
|
|
return station;
|
|
}).toList();
|
|
return result;
|
|
}
|
|
Map<String, dynamic> toJson() => _$TrainDataToJson(this);
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class LastInfo {
|
|
@JsonKey(name: "statia")
|
|
final String station;
|
|
@JsonKey(name: "eveniment")
|
|
final String event;
|
|
@JsonKey(name: "data_si_ora")
|
|
final String dateAndTime;
|
|
DateTime get formattedDateAndTime {
|
|
return parseCFRDateTime(dateAndTime);
|
|
}
|
|
@JsonKey(name: "intarziere")
|
|
final int delay;
|
|
|
|
|
|
LastInfo({this.dateAndTime, this.delay, this.event, this.station});
|
|
|
|
factory LastInfo.fromJson(Map<String, dynamic> json) => _$LastInfoFromJson(json);
|
|
Map<String, dynamic> toJson() => _$LastInfoToJson(this);
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class StopInfo {
|
|
@JsonKey(name: "statia")
|
|
final String station;
|
|
@JsonKey(name: "data_si_ora")
|
|
final String dateAndTime;
|
|
DateTime get formattedDateAndTime {
|
|
return parseCFRDateTime(dateAndTime);
|
|
}
|
|
|
|
StopInfo({this.station, this.dateAndTime});
|
|
|
|
factory StopInfo.fromJson(Map<String, dynamic> json) => _$StopInfoFromJson(json);
|
|
Map<String, dynamic> toJson() => _$StopInfoToJson(this);
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class StationEntry {
|
|
final String km;
|
|
@JsonKey(name: "statia")
|
|
final String name;
|
|
@JsonKey(name: "sosire")
|
|
final String arrivalTime;
|
|
@JsonKey(name: "stationeaza_pentru")
|
|
final String waitTime;
|
|
@JsonKey(name: "plecare")
|
|
final String departureTime;
|
|
@JsonKey(name: "real/estimat")
|
|
String realOrEstimate;
|
|
bool get real {
|
|
return realOrEstimate == "Real";
|
|
}
|
|
@JsonKey(name: "intarziere")
|
|
final int delay;
|
|
@JsonKey(name: "observatii")
|
|
final String observations;
|
|
|
|
StationEntry({this.name, this.delay, this.realOrEstimate,
|
|
this.arrivalTime, this.departureTime, this.km, this.observations,
|
|
this.waitTime});
|
|
|
|
factory StationEntry.fromJson(Map<String, dynamic> json) => _$StationEntryFromJson(json);
|
|
Map<String, dynamic> toJson() => _$StationEntryToJson(this);
|
|
}
|
|
|
|
DateTime parseCFRDateTime(String dateAndTime) {
|
|
final parts = dateAndTime.split(" ");
|
|
|
|
final dateParts = parts[0].split(".");
|
|
final day = int.parse(dateParts[0]);
|
|
final month = int.parse(dateParts[1]);
|
|
final year = int.parse(dateParts[2]);
|
|
|
|
final timeParts = parts[1].split(":");
|
|
final hour = int.parse(timeParts[0]);
|
|
final minute = int.parse(timeParts[1]);
|
|
|
|
return DateTime(year, month, day, hour, minute);
|
|
} |