Public Access
feat(transporthelper): code cleanup, add toMaps, implement m-n tables
- remove final keywords everywhere for now cause I don't think I need them yet for any specific purpose - add toMap() functions - add database.insert() functions for classes and relational tables when classes use list that reference other classes
This commit is contained in:
+161
-106
@@ -37,120 +37,106 @@ class DbHelper {
|
|||||||
Station.initStationTable();
|
Station.initStationTable();
|
||||||
Vehicle.initVehicleTable();
|
Vehicle.initVehicleTable();
|
||||||
Departure.initDepartureTable();
|
Departure.initDepartureTable();
|
||||||
Route.updateHighestId();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// this class covers two database tables: "route" and "routesegments"
|
||||||
|
/// while route will for now only hold an id, routesegments will store the
|
||||||
|
/// m-n/ 1-n relations between routes and segments - the segments contain the most
|
||||||
|
/// relevant info, while routesegments stores which route the segments belong to
|
||||||
|
/// and in which order they make up a route
|
||||||
class Route {
|
class Route {
|
||||||
static int highestId = 0;
|
// TODO: add last_viewed, last_modified and such
|
||||||
static const String tableName = "route";
|
int? id;
|
||||||
|
|
||||||
int id;
|
|
||||||
List<Segment> segments = List.empty();
|
List<Segment> segments = List.empty();
|
||||||
Duration duration = Duration(minutes: 0);
|
Duration duration = Duration(minutes: 0);
|
||||||
|
|
||||||
Route({
|
Route({
|
||||||
required this.id,
|
this.id,
|
||||||
required this.segments,
|
required this.segments,
|
||||||
required this.duration,
|
required this.duration,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// toMap for Route but it's just route ids for now
|
||||||
|
Map<String, Object?> toMapRoute() {
|
||||||
|
return {"id": id};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// toMap for routesegments relational table
|
||||||
|
List<Map<String, Object?>> toMapRouteSegments() {
|
||||||
|
//asMap() apparently enumerates
|
||||||
|
List<Map<String, Object?>> tmp = List.empty();
|
||||||
|
for (final (segmentIdx, segment) in segments.indexed) {
|
||||||
|
tmp.add({
|
||||||
|
"route": id,
|
||||||
|
"segmentid": segment.id,
|
||||||
|
"segmentorder": segmentIdx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
// from trip API request
|
// from trip API request
|
||||||
// TODO: how to connect this best with the database for read/write?
|
// TODO: how to connect this best with the database for read/write?
|
||||||
factory Route.fromJson(Map<String, dynamic> json) {
|
factory Route.fromJson(Map<String, dynamic> json) {
|
||||||
Route newRoute = Route(
|
Route newRoute = Route(
|
||||||
id: highestId,
|
|
||||||
segments: json["LegList"]["Leg"].map((leg) => Segment.fromJson(leg)),
|
segments: json["LegList"]["Leg"].map((leg) => Segment.fromJson(leg)),
|
||||||
duration:
|
duration:
|
||||||
json["LegList"]["Leg"][-1]["Origin"]["time"] -
|
json["LegList"]["Leg"][-1]["Origin"]["time"] -
|
||||||
json["LegList"]["Leg"][0]["Origin"]["time"],
|
json["LegList"]["Leg"][0]["Origin"]["time"],
|
||||||
);
|
);
|
||||||
highestId += 1;
|
|
||||||
return newRoute;
|
return newRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rowids will give us new ids for every route-segment connection, but routeid
|
|
||||||
// is needed as well, so how do we create these - query highest existing routeid? I think so
|
|
||||||
// TODO: get highest routeid in route table (fast)
|
|
||||||
static void initRouteTable() async {
|
static void initRouteTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
database.execute("""CREATE TABLE IF NOT EXISTS routesegments(
|
database.execute("""CREATE TABLE IF NOT EXISTS route(
|
||||||
id TEXT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
segmentid INTEGER,
|
|
||||||
segmentorder INTEGER, -- TODO: implement sorting
|
|
||||||
FOREIGN KEY(segmentid) REFERENCES segment(id)
|
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateHighestId() async {
|
static void initRouteSegmentsTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
int newHigh = await getHighestId();
|
String segment = Segment.tableName;
|
||||||
highestId = newHigh + 1;
|
database.execute("""CREATE TABLE IF NOT EXISTS routesegments(
|
||||||
}
|
id TEXT PRIMARY KEY AUTOINCREMENT,
|
||||||
|
route INTEGER,
|
||||||
/* TODO: better separation between production db and test db
|
segment INTEGER,
|
||||||
* they share the highestId field regardless, which wont do
|
segmentorder INTEGER, -- TODO: implement sorting
|
||||||
* so is an attribut for database needed?
|
FOREIGN KEY(route) REFERENCES route(id),
|
||||||
*/
|
FOREIGN KEY(segment) REFERENCES $segment(id)
|
||||||
|
|
||||||
/// parse the highest id from the database to continue with ongoing ids but
|
|
||||||
/// create the db entry after the class, for now
|
|
||||||
|
|
||||||
// -- this might be making it more complicated than it needs to be
|
|
||||||
static Future<int> getHighestId() async {
|
|
||||||
Database database = DbHelper.db;
|
|
||||||
try {
|
|
||||||
var tmp = await database.query(
|
|
||||||
"routesegments",
|
|
||||||
columns: ["id"],
|
|
||||||
distinct: true,
|
|
||||||
orderBy: "id",
|
|
||||||
whereArgs: ["id"],
|
|
||||||
limit: 1,
|
|
||||||
);
|
);
|
||||||
if (tmp.isEmpty) {
|
""");
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (tmp[0].isEmpty) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// entry "routeid" should definitely exist
|
|
||||||
assert(tmp[0]["id"] != null);
|
|
||||||
|
|
||||||
// Source - https://stackoverflow.com/a/52632172
|
|
||||||
// Posted by Günter Zöchbauer, modified by community. See post 'Timeline' for change history
|
|
||||||
// Retrieved 2026-06-08, License - CC BY-SA 4.0
|
|
||||||
T? cast<T>(x) => x is T ? x : null;
|
|
||||||
// --
|
|
||||||
//doing this in two steps go deal with nullability
|
|
||||||
String highestId = cast<String>(tmp[0]["routeid"]) ?? "0";
|
|
||||||
return int.parse(highestId);
|
|
||||||
} catch (e) {
|
|
||||||
print('Error: Could not get id: $e'); // TODO: use logging
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeNewToTable(Route route) async {
|
void writeRouteToTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
for (var i = 0; i < route.segments.length; i++) {
|
database.insert(
|
||||||
database.insert("routesegments", {
|
"route",
|
||||||
"id": route.id,
|
toMapRoute(),
|
||||||
"segmentid": route.segments[i].id,
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
"segmentorder": i,
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
void writeRouteSegmentsToTable() async {
|
||||||
|
Database database = DbHelper.db;
|
||||||
|
var routeSegments = toMapRouteSegments();
|
||||||
|
for (var i = 0; i < routeSegments.length; i++) {
|
||||||
|
database.insert("routesegments", routeSegments[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Segment {
|
class Segment {
|
||||||
final int id = 0;
|
static const String tableName = "segment";
|
||||||
final Station startPoint;
|
|
||||||
final Station endPoint;
|
int? id;
|
||||||
final DateTime startTime;
|
Station startPoint;
|
||||||
final DateTime endTime;
|
Station endPoint;
|
||||||
final Vehicle vehicle;
|
DateTime startTime;
|
||||||
|
DateTime endTime;
|
||||||
|
Vehicle vehicle;
|
||||||
|
|
||||||
Segment({
|
Segment({
|
||||||
required this.startPoint,
|
required this.startPoint,
|
||||||
@@ -160,6 +146,16 @@ class Segment {
|
|||||||
required this.vehicle,
|
required this.vehicle,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Map<String, Object?> toMap() {
|
||||||
|
return {
|
||||||
|
"startPoint": startPoint.id,
|
||||||
|
"endpoint": endPoint.id,
|
||||||
|
"starttime": startTime,
|
||||||
|
"endtime": endTime,
|
||||||
|
"vehicle": vehicle.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
factory Segment.fromJson(Map<String, dynamic> json) {
|
factory Segment.fromJson(Map<String, dynamic> json) {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
Segment tmp = Segment(
|
Segment tmp = Segment(
|
||||||
@@ -175,37 +171,35 @@ class Segment {
|
|||||||
|
|
||||||
static void initSegmentTable() async {
|
static void initSegmentTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
|
String station = Station.tableName;
|
||||||
|
String vehicle = Vehicle.tableName;
|
||||||
database.execute("""
|
database.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS segment(
|
CREATE TABLE IF NOT EXISTS $tableName(
|
||||||
startpoint TEXT,
|
startpoint TEXT,
|
||||||
endpoint TEXT,
|
endpoint TEXT,
|
||||||
starttime TEXT,
|
starttime TEXT,
|
||||||
endtime TEXT,
|
endtime TEXT,
|
||||||
vehicle INTEGER,
|
vehicle INTEGER,
|
||||||
FOREIGN KEY(startpoint) REFERENCES station(stationid),
|
FOREIGN KEY(startpoint) REFERENCES $station(stationid),
|
||||||
FOREIGN KEY(endpoint) REFERENCES station(stationid),
|
FOREIGN KEY(endpoint) REFERENCES $station(stationid),
|
||||||
FOREIGN KEY(vehicle) REFERENCES vehicle(vehicleid)
|
FOREIGN KEY(vehicle) REFERENCES $vehicle(vehicleid)
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> writeNewToTable() async {
|
Future<int> writeNewToTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
int id = await database.insert("segment", {
|
int id = await database.insert(tableName, toMap());
|
||||||
"startpoint": startPoint.id,
|
|
||||||
"endpoint": startPoint.id,
|
|
||||||
"startTime": startTime,
|
|
||||||
"endTime": endTime,
|
|
||||||
"vehicle": vehicle.id,
|
|
||||||
});
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Station {
|
class Station {
|
||||||
final String id;
|
static const String tableName = "station";
|
||||||
final String name;
|
|
||||||
List<Line>? transportLines; // could be normalized away
|
String id;
|
||||||
|
String name;
|
||||||
|
List<Line> transportLines; // could be normalized away
|
||||||
|
|
||||||
Station({
|
Station({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -213,6 +207,18 @@ class Station {
|
|||||||
required this.transportLines, //TODO: make extra db table
|
required this.transportLines, //TODO: make extra db table
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Map<String, Object?> toMap() {
|
||||||
|
return {"id": id, "name": name};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object?>> toMapLines() {
|
||||||
|
return transportLines
|
||||||
|
.map(
|
||||||
|
(line) => {"stationid": id, "lineid": line},
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
factory Station.fromJson(Map<String, dynamic> json) => Station(
|
factory Station.fromJson(Map<String, dynamic> json) => Station(
|
||||||
id: json["id"] as String? ?? json["extId"] as String,
|
id: json["id"] as String? ?? json["extId"] as String,
|
||||||
name: json["name"],
|
name: json["name"],
|
||||||
@@ -226,22 +232,43 @@ class Station {
|
|||||||
static void initStationTable() async {
|
static void initStationTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
database.execute("""CREATE TABLE IF NOT EXISTS station(
|
database.execute("""CREATE TABLE IF NOT EXISTS station(
|
||||||
id TEXT PRIMARY KEY,
|
stationid TEXT PRIMARY KEY,
|
||||||
name TEXT
|
name TEXT
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void initStationLinesTable() async {
|
||||||
|
Database database = DbHelper.db;
|
||||||
|
database.execute("""CREATE TABLE IF NOT EXISTS stationlines(
|
||||||
|
id INT PRIMARY KEY AUTOINCREMENT,
|
||||||
|
stationid TEXT,
|
||||||
|
lineid TEXT,
|
||||||
|
FOREIGN KEY (stationid) REFERENCES station(stationid),
|
||||||
|
FOREIGN KEY (lineid) REFERENCES line(lineid)
|
||||||
|
);
|
||||||
|
""");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Vehicle {
|
class Vehicle {
|
||||||
final String id;
|
static const String tableName = "vehicle";
|
||||||
final Line line;
|
|
||||||
|
String id;
|
||||||
|
Line line;
|
||||||
|
|
||||||
Vehicle({
|
Vehicle({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.line,
|
required this.line,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Map<String, Object?> toMap() {
|
||||||
|
return {
|
||||||
|
"id": id,
|
||||||
|
"line": line.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// we can use the "product" class from within any JSON response
|
// we can use the "product" class from within any JSON response
|
||||||
// (within hafas API, on the appropriate level)
|
// (within hafas API, on the appropriate level)
|
||||||
factory Vehicle.fromJson(Map<String, dynamic> json) => Vehicle(
|
factory Vehicle.fromJson(Map<String, dynamic> json) => Vehicle(
|
||||||
@@ -251,21 +278,24 @@ class Vehicle {
|
|||||||
|
|
||||||
static void initVehicleTable() async {
|
static void initVehicleTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
database.execute("""CREATE TABLE IF NOT EXISTS vehicle(
|
String line = Line.tableName;
|
||||||
|
database.execute("""CREATE TABLE IF NOT EXISTS $tableName(
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
line TEXT,
|
line TEXT,
|
||||||
FOREIGN KEY(line) REFERENCES line(lineid)
|
FOREIGN KEY(line) REFERENCES $line(lineid)
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Line {
|
class Line {
|
||||||
final String id;
|
static const String tableName = "line";
|
||||||
|
|
||||||
|
String id;
|
||||||
Station? destination;
|
Station? destination;
|
||||||
Station? direction;
|
Station? direction;
|
||||||
final VehicleType mode;
|
VehicleType mode;
|
||||||
final String name;
|
String name;
|
||||||
Color? fgColor;
|
Color? fgColor;
|
||||||
Color? bgColor;
|
Color? bgColor;
|
||||||
|
|
||||||
@@ -277,6 +307,18 @@ class Line {
|
|||||||
this.bgColor,
|
this.bgColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Map<String, Object?> toMap() {
|
||||||
|
return {
|
||||||
|
"lineid": id,
|
||||||
|
"destination": destination,
|
||||||
|
"direction": direction,
|
||||||
|
"mode": mode,
|
||||||
|
"name": name,
|
||||||
|
"fgcolor": fgColor,
|
||||||
|
"bgcolor": bgColor,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: EXPLAIN!!!!!!!!!!!!
|
// TODO: EXPLAIN!!!!!!!!!!!!
|
||||||
static const _vehicleTypeAsCatCode = [
|
static const _vehicleTypeAsCatCode = [
|
||||||
VehicleType.metro,
|
VehicleType.metro,
|
||||||
@@ -298,7 +340,7 @@ class Line {
|
|||||||
);
|
);
|
||||||
static void initLineTable() async {
|
static void initLineTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
database.execute("""CREATE TABLE IF NOT EXISTS line(
|
database.execute("""CREATE TABLE IF NOT EXISTS $tableName(
|
||||||
lineid TEXT PRIMARY KEY,
|
lineid TEXT PRIMARY KEY,
|
||||||
destination TEXT,
|
destination TEXT,
|
||||||
direction TEXT,
|
direction TEXT,
|
||||||
@@ -312,10 +354,12 @@ class Line {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Departure {
|
class Departure {
|
||||||
|
static const String tableName = "departure";
|
||||||
|
|
||||||
String? id;
|
String? id;
|
||||||
final Vehicle vehicle;
|
Vehicle vehicle;
|
||||||
final Station station;
|
Station station;
|
||||||
final DateTime departureTime;
|
DateTime departureTime;
|
||||||
|
|
||||||
Departure({
|
Departure({
|
||||||
required this.vehicle,
|
required this.vehicle,
|
||||||
@@ -323,24 +367,35 @@ class Departure {
|
|||||||
required this.departureTime,
|
required this.departureTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Map<String, Object?> toMap() {
|
||||||
|
return {
|
||||||
|
"id": id,
|
||||||
|
"vehicle": vehicle,
|
||||||
|
"station": station,
|
||||||
|
"departuretime": departureTime,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static void initDepartureTable() async {
|
static void initDepartureTable() async {
|
||||||
Database database = DbHelper.db;
|
Database database = DbHelper.db;
|
||||||
database.execute("""CREATE TABLE IF NOT EXISTS departure(
|
String vehicle = Vehicle.tableName;
|
||||||
|
String station = Station.tableName;
|
||||||
|
database.execute("""CREATE TABLE IF NOT EXISTS $tableName(
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
vehicle TEXT,
|
vehicle TEXT,
|
||||||
station TEXT,
|
station TEXT,
|
||||||
departuretime TEXT,
|
departuretime TEXT,
|
||||||
FOREIGN KEY(vehicle) REFERENCES vehicle(id),
|
FOREIGN KEY(vehicle) REFERENCES $vehicle(id),
|
||||||
FOREIGN KEY(station) REFERENCES station(id)
|
FOREIGN KEY(station) REFERENCES $station(id)
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Color {
|
class Color {
|
||||||
final int r;
|
int r;
|
||||||
final int g;
|
int g;
|
||||||
final int b;
|
int b;
|
||||||
|
|
||||||
Color({
|
Color({
|
||||||
required this.r,
|
required this.r,
|
||||||
|
|||||||
Reference in New Issue
Block a user