feat(classes): implement initiation, start work on dbHelper

This commit is contained in:
fbachus
2026-06-16 00:48:53 +02:00
parent 7cfc415f9f
commit 7f6c7beb59
2 changed files with 178 additions and 53 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ import 'transport_helper.dart';
void main() async { void main() async {
await dotenv.load(); await dotenv.load();
const String dbName = "oeffimasterTransportDb"; const String dbName = "oeffimasterTransportDb";
initDB(dbName); DbHelper.initDb(dbName);
runApp(const MyApp()); runApp(const MyApp());
} }
+176 -51
View File
@@ -1,48 +1,151 @@
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
// import './state_helper.dart';
// import './db_helper.dart';
void initDB(dbName) async { // TODO: get access to DB
var databasesPath = await getDatabasesPath();
var path = databasesPath + dbName;
var db = await openDatabase(path);
// var db = await openDatabase('/var/db/oeffimaster/transport_db.db');
// TODO: Make sure the directory exists class DbHelper {
static late Database db;
static String dbName = "";
// TODO: future optimisation: batch transactions // DbHelper({
// required this.db,
// required this.dbName,
// });
// TODO: create tables for all classes // remnant from when DB was not static
// - [] route // factory DbHelper._openDb(Database database, String dbName) => DbHelper(
// - [] segment // db: database,
// - [] station // dbName: dbName,
// - [] vehicle // );
// - [] line
// - [] departure 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();
Route.updateHighestId();
}
} }
class Route { class Route {
static int highestId = 0;
static const String tableName = "route";
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,
required this.segments, required this.segments,
required this.duration, required this.duration,
}); });
// from trip API request // from trip API request
factory Route.fromJson(Map<String, dynamic> json) => Route( // TODO: how to connect this best with the database for read/write?
segments: json["LegList"]["Leg"].map((leg) => Segment.fromJson(leg)), factory Route.fromJson(Map<String, dynamic> json) {
duration: Route newRoute = Route(
json["LegList"]["Leg"][-1]["Origin"]["time"] - id: highestId,
json["LegList"]["Leg"][0]["Origin"]["time"], segments: json["LegList"]["Leg"].map((leg) => Segment.fromJson(leg)),
); duration:
json["LegList"]["Leg"][-1]["Origin"]["time"] -
json["LegList"]["Leg"][0]["Origin"]["time"],
);
highestId += 1;
return newRoute;
}
// TODO: do we need route in the database???? // what beyond and ID is needed? // rowids will give us new ids for every route-segment connection, but routeid
void initRouteTable(Database database) async { // is needed as well, so how do we create these - query highest existing routeid? I think so
database.execute("CREATE TABLE IF NOT EXISTS route();"); // TODO: get highest routeid in route table (fast)
static void initRouteTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS routesegments(
id TEXT,
segmentid INTEGER,
segmentorder INTEGER, -- TODO: implement sorting
FOREIGN KEY(segmentid) REFERENCES segment(id)
);
""");
}
static void updateHighestId() async {
Database database = DbHelper.db;
int newHigh = await getHighestId();
highestId = newHigh + 1;
}
/* TODO: better separation between production db and test db
* they share the highestId field regardless, which wont do
* so is an attribut for database needed?
*/
/// 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 {
Database database = DbHelper.db;
for (var i = 0; i < route.segments.length; i++) {
database.insert("routesegments", {
"id": route.id,
"segmentid": route.segments[i].id,
"segmentorder": i,
});
}
} }
} }
class Segment { class Segment {
final int id = 0;
final Station startPoint; final Station startPoint;
final Station endPoint; final Station endPoint;
final DateTime startTime; final DateTime startTime;
@@ -57,15 +160,21 @@ class Segment {
required this.vehicle, required this.vehicle,
}); });
factory Segment.fromJson(Map<String, dynamic> json) => Segment( factory Segment.fromJson(Map<String, dynamic> json) {
startPoint: Station.fromJson(json["Origin"]), Database database = DbHelper.db;
endPoint: Station.fromJson(json["Destination"]), Segment tmp = Segment(
startTime: json["Origin"]["rtTime"], startPoint: Station.fromJson(json["Origin"]),
endTime: json["Destination"]["rtTime"], endPoint: Station.fromJson(json["Destination"]),
vehicle: Vehicle.fromJson(json["Product"]), startTime: json["Origin"]["rtTime"],
); endTime: json["Destination"]["rtTime"],
vehicle: Vehicle.fromJson(json["Product"]),
);
tmp.writeNewToTable(); // TODO: we need to write to db and get the ID!!!!!!!
return tmp;
}
void initSegmentTable(Database database) async { static void initSegmentTable() async {
Database database = DbHelper.db;
database.execute(""" database.execute("""
CREATE TABLE IF NOT EXISTS segment( CREATE TABLE IF NOT EXISTS segment(
startpoint TEXT, startpoint TEXT,
@@ -74,26 +183,38 @@ class Segment {
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 {
Database database = DbHelper.db;
int id = await database.insert("segment", {
"startpoint": startPoint.id,
"endpoint": startPoint.id,
"startTime": startTime,
"endTime": endTime,
"vehicle": vehicle.id,
});
return id;
}
} }
class Station { class Station {
final String stationId; final String id;
final String name; final String name;
List<Line>? transportLines; // could be normalized away List<Line>? transportLines; // could be normalized away
Station({ Station({
required this.stationId, required this.id,
required this.name, required this.name,
required this.transportLines, //TODO: make extra db table required this.transportLines, //TODO: make extra db table
}); });
factory Station.fromJson(Map<String, dynamic> json) => Station( factory Station.fromJson(Map<String, dynamic> json) => Station(
stationId: json["id"] as String? ?? json["extId"] as String, id: json["id"] as String? ?? json["extId"] as String,
name: json["name"], name: json["name"],
transportLines: json.containsKey("productAtStop") transportLines: json.containsKey("productAtStop")
? json["productAtStop"].map( ? json["productAtStop"].map(
@@ -102,9 +223,10 @@ class Station {
: const [], : const [],
); );
void initStationTable(Database database) async { static void initStationTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS station( database.execute("""CREATE TABLE IF NOT EXISTS station(
stationid TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
name TEXT name TEXT
); );
"""); """);
@@ -112,24 +234,25 @@ class Station {
} }
class Vehicle { class Vehicle {
final String vehicleId; final String id;
final Line line; final Line line;
Vehicle({ Vehicle({
required this.vehicleId, required this.id,
required this.line, required this.line,
}); });
// 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(
vehicleId: json["matchId"], id: json["matchId"],
line: Line.fromJson(json), line: Line.fromJson(json),
); );
void initVehicleTable(Database database) async { static void initVehicleTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS vehicle( database.execute("""CREATE TABLE IF NOT EXISTS vehicle(
vehicleid TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
line TEXT, line TEXT,
FOREIGN KEY(line) REFERENCES line(lineid) FOREIGN KEY(line) REFERENCES line(lineid)
); );
@@ -138,7 +261,7 @@ class Vehicle {
} }
class Line { class Line {
final String lineId; final String id;
Station? destination; Station? destination;
Station? direction; Station? direction;
final VehicleType mode; final VehicleType mode;
@@ -147,7 +270,7 @@ class Line {
Color? bgColor; Color? bgColor;
Line({ Line({
required this.lineId, required this.id,
required this.name, required this.name,
required this.mode, required this.mode,
this.fgColor, this.fgColor,
@@ -163,7 +286,7 @@ class Line {
]; ];
factory Line.fromJson(Map<String, dynamic> json) => Line( factory Line.fromJson(Map<String, dynamic> json) => Line(
lineId: json["lineId"], id: json["lineId"],
name: json["name"], name: json["name"],
mode: _vehicleTypeAsCatCode[int.parse(json["catCode"])], mode: _vehicleTypeAsCatCode[int.parse(json["catCode"])],
fgColor: fgColor:
@@ -173,7 +296,8 @@ class Line {
Color.fromJson(json["backgroundColor"]) as Color? ?? Color.fromJson(json["backgroundColor"]) as Color? ??
Color(r: 50, g: 50, b: 50), // fallback Color(r: 50, g: 50, b: 50), // fallback
); );
void initLineTable(Database database) async { static void initLineTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS line( database.execute("""CREATE TABLE IF NOT EXISTS line(
lineid TEXT PRIMARY KEY, lineid TEXT PRIMARY KEY,
destination TEXT, destination TEXT,
@@ -199,14 +323,15 @@ class Departure {
required this.departureTime, required this.departureTime,
}); });
void initDepartureTable(Database database) async { static void initDepartureTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS departure( database.execute("""CREATE TABLE IF NOT EXISTS departure(
departureid INTEGER, id INTEGER,
vehicle TEXT, vehicle TEXT,
station TEXT, station TEXT,
departuretime TEXT departuretime TEXT,
FOREIGN KEY(vehicle) REFERENCES vehicle(vehicleid), FOREIGN KEY(vehicle) REFERENCES vehicle(id),
FOREIGN KEY(station) REFERENCES station(stationid) FOREIGN KEY(station) REFERENCES station(id)
); );
"""); """);
} }