Files
oeffimaster/lib/transport_helper.dart
T
fbachus 704e640495 style(transport_helper): rename db_insert functions
On classes with relational tables, the insert function for the table
that matches the actual class (not relation), is now just called dbInsert
instead of dbInsertClassname.
For inserting to relational Tables, the name stays more descriptive
about the relation covered
2026-07-03 00:29:19 +02:00

522 lines
13 KiB
Dart

import 'package:sqflite/sqflite.dart';
// import './state_helper.dart';
// import './db_helper.dart';
// TODO: get access to DB
class DbHelper {
static late Database db;
static String dbName = "";
// DbHelper({
// required this.db,
// required this.dbName,
// });
// remnant from when DB was not static
// factory DbHelper._openDb(Database database, String dbName) => DbHelper(
// db: database,
// dbName: dbName,
// );
static void initDb(String dbName) async {
// var db = await openDatabase('/var/db/oeffimaster/transport_db.db');
var databasesPath = await getDatabasesPath();
var path = databasesPath + dbName;
// var newDb = await openDatabase(path);
// var dbHelper = DbHelper._openDb(newDb, dbName);
db = await openDatabase(path);
dbName = dbName;
// TODO: Make sure the directory exists
// TODO: future optimisation: batch transactions
Route.initRouteTable();
Segment.initSegmentTable();
Station.initStationTable();
Vehicle.initVehicleTable();
Departure.initDepartureTable();
}
}
/// 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 {
// TODO: add last_viewed, last_modified and such
int? id;
List<Segment> segments = List.empty();
Duration duration = Duration(minutes: 0);
Route({
this.id,
required this.segments,
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
// TODO: how to connect this best with the database for read/write?
factory Route.fromJson(Map<String, dynamic> json) {
Route newRoute = Route(
segments: json["LegList"]["Leg"].map((leg) => Segment.fromJson(leg)),
duration:
json["LegList"]["Leg"][-1]["Origin"]["time"] -
json["LegList"]["Leg"][0]["Origin"]["time"],
);
newRoute.dbInsertRoute();
return newRoute;
}
static void initRouteTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS route(
id INTEGER PRIMARY KEY AUTOINCREMENT,
);
""");
}
static void initRouteSegmentsTable() async {
Database database = DbHelper.db;
String segment = Segment.tableName;
database.execute("""CREATE TABLE IF NOT EXISTS routesegments(
id TEXT PRIMARY KEY AUTOINCREMENT,
route INTEGER,
segment INTEGER,
segmentorder INTEGER, -- TODO: implement sorting
FOREIGN KEY(route) REFERENCES route(id),
FOREIGN KEY(segment) REFERENCES $segment(id)
);
""");
}
void dbInsert() async {
Database database = DbHelper.db;
id = await database.insert(
"route",
toMapRoute(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
void dbInsertRouteSegments() async {
Database database = DbHelper.db;
var routeSegments = toMapRouteSegments();
for (var i = 0; i < routeSegments.length; i++) {
database.insert(
"routesegments",
routeSegments[i],
);
}
}
void dbDelete() async {
Database database = DbHelper.db;
int result = await database.delete(
"route",
where: "id = ?",
whereArgs: [id],
);
assert(result == 0);
}
void dbDeleteLastRouteSegment() {
Database database = DbHelper.db;
database.delete(
"routesegments",
where: "route = ? AND segmentorder = ?",
whereArgs: [id, segments.length - 1], // TODO: test the index
);
}
void dbDeleteTrailingRouteSegment(int position) {
Database database = DbHelper.db;
if (position <= segments.length) {
database.delete(
"routesegments",
where: "route = ? AND segmentorder >= ?",
whereArgs: [id, position],
);
}
}
// TODO: delete leading segments and decrement following segments
}
class Segment {
static const String tableName = "segment";
int? id;
Station startPoint;
Station endPoint;
DateTime startTime;
DateTime endTime;
Vehicle vehicle;
Segment({
this.id, // might throw errors - needs testing, I guess
required this.startPoint,
required this.endPoint,
required this.startTime,
required this.endTime,
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) {
Database database = DbHelper.db;
Segment tmp = Segment(
startPoint: Station.fromJson(json["Origin"]),
endPoint: Station.fromJson(json["Destination"]),
startTime: json["Origin"]["rtTime"],
endTime: json["Destination"]["rtTime"],
vehicle: Vehicle.fromJson(json["Product"]),
);
tmp.dbInsert();
return tmp;
}
static void initSegmentTable() async {
Database database = DbHelper.db;
String station = Station.tableName;
String vehicle = Vehicle.tableName;
database.execute("""
CREATE TABLE IF NOT EXISTS $tableName(
startpoint TEXT,
endpoint TEXT,
starttime TEXT,
endtime TEXT,
vehicle INTEGER,
FOREIGN KEY(startpoint) REFERENCES $station(stationid),
FOREIGN KEY(endpoint) REFERENCES $station(stationid),
FOREIGN KEY(vehicle) REFERENCES $vehicle(vehicleid)
);
""");
}
void dbInsert() async {
Database database = DbHelper.db;
id = await database.insert(tableName, toMap());
}
void dbDelete() {
Database database = DbHelper.db;
database.delete(
tableName,
where: "rowid = ?",
whereArgs: [id],
);
}
}
class Station {
static const String tableName = "station";
String id;
String name;
List<Line> transportLines; // could be normalized away
Station({
required this.id,
required this.name,
required this.transportLines,
});
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(
id: json["id"] as String? ?? json["extId"] as String,
name: json["name"],
transportLines: json.containsKey("productAtStop")
? json["productAtStop"].map(
(jsonLine) => Line.fromJson(jsonLine as Map<String, dynamic>),
)
: const [],
);
static void initStationTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS station(
stationid TEXT PRIMARY KEY,
name TEXT
);
""");
}
static void initStationLinesTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS stationlines(
id INTEGER PRIMARY KEY AUTOINCREMENT,
stationid TEXT,
lineid TEXT,
FOREIGN KEY (stationid) REFERENCES station(stationid),
FOREIGN KEY (lineid) REFERENCES line(lineid)
);
""");
}
void dbInsert() async {
Database database = DbHelper.db;
database.insert(tableName, toMap());
}
void dbInsertStationLines() async {
Database database = DbHelper.db;
var stationLines = toMapLines();
for (var i = 0; i < stationLines.length; i++) {
database.insert("stationlines", stationLines[i]);
}
}
void dbDelete() async {
Database database = DbHelper.db;
database.delete(tableName, where: "stationid = ?", whereArgs: [id]);
}
/// remove all Lines connected to a Station - should be used together with some kind of reparsing.
void dbDeleteStationLines() async {
Database database = DbHelper.db;
database.delete("stationlines", where: "stationid = ?", whereArgs: [id]);
}
}
class Vehicle {
static const String tableName = "vehicle";
String id;
Line line;
Vehicle({
required this.id,
required this.line,
});
Map<String, Object?> toMap() {
return {
"id": id,
"line": line.lineId,
};
}
// we can use the "product" class from within any JSON response
// (within hafas API, on the appropriate level)
factory Vehicle.fromJson(Map<String, dynamic> json) => Vehicle(
id: json["matchId"],
line: Line.fromJson(json),
);
static void initVehicleTable() async {
Database database = DbHelper.db;
String line = Line.tableName;
database.execute("""CREATE TABLE IF NOT EXISTS $tableName(
id TEXT PRIMARY KEY,
line TEXT,
FOREIGN KEY(line) REFERENCES $line(lineid)
);
""");
}
void dbInsert() async {
Database database = DbHelper.db;
database.insert(tableName, toMap());
}
void dbDelete() async {
Database database = DbHelper.db;
database.delete(tableName, where: "id = ?", whereArgs: [id]);
}
}
class Line {
static const String tableName = "line";
String lineId;
Station? destination;
Station? direction;
VehicleType mode;
String name;
Color? fgColor;
Color? bgColor;
Line({
required this.lineId,
required this.name,
required this.mode,
this.fgColor,
this.bgColor,
});
Map<String, Object?> toMap() {
return {
"lineid": lineId,
"destination": destination,
"direction": direction,
"mode": mode,
"name": name,
"fgcolor": fgColor,
"bgcolor": bgColor,
};
}
// TODO: EXPLAIN!!!!!!!!!!!!
static const _vehicleTypeAsCatCode = [
VehicleType.metro,
VehicleType.subway,
VehicleType.PLACEHOLDER,
VehicleType.bus,
];
factory Line.fromJson(Map<String, dynamic> json) => Line(
lineId: json["lineId"],
name: json["name"],
mode: _vehicleTypeAsCatCode[int.parse(json["catCode"])],
fgColor:
Color.fromJson(json["foregroundColor"]) as Color? ??
Color(r: 255, g: 255, b: 255), // fallback
bgColor:
Color.fromJson(json["backgroundColor"]) as Color? ??
Color(r: 50, g: 50, b: 50), // fallback
);
static void initLineTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS $tableName(
lineid TEXT PRIMARY KEY,
destination TEXT,
direction TEXT,
mode TEXT,
name TEXT,
fgcolor TEXT,
bgcolor TEXT
);
""");
}
void dbInsert() async {
Database database = DbHelper.db;
database.insert(tableName, toMap());
}
void dbDelete() async {
Database database = DbHelper.db;
database.delete(tableName, where: "lineid = ?", whereArgs: [lineId]);
}
}
// TODO: add fromJson
class Departure {
static const String tableName = "departure";
int? id;
Vehicle vehicle;
Station station;
DateTime departureTime;
Departure({
this.id,
required this.vehicle,
required this.station,
required this.departureTime,
});
Map<String, Object?> toMap() {
return {
"id": id,
"vehicle": vehicle,
"station": station,
"departuretime": departureTime,
};
}
static void initDepartureTable() async {
Database database = DbHelper.db;
String vehicle = Vehicle.tableName;
String station = Station.tableName;
database.execute("""CREATE TABLE IF NOT EXISTS $tableName(
id INTEGER,
vehicle TEXT,
station TEXT,
departuretime TEXT,
FOREIGN KEY(vehicle) REFERENCES $vehicle(id),
FOREIGN KEY(station) REFERENCES $station(id)
);
""");
}
void dbInsert() async {
Database database = DbHelper.db;
id = await database.insert(tableName, toMap());
}
void dbDelete() async {
Database database = DbHelper.db;
database.delete(tableName, where: "id = ?", whereArgs: [id]);
}
}
class Color {
int r;
int g;
int b;
Color({
required this.r,
required this.g,
required this.b,
});
factory Color.fromJson(Map<String, dynamic> json) => Color(
r: json["r"],
g: json["g"],
b: json["b"],
);
}
enum VehicleType {
bus,
tram,
metro,
subway,
regionalTrain,
PLACEHOLDER,
}