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 {
await dotenv.load();
const String dbName = "oeffimasterTransportDb";
initDB(dbName);
DbHelper.initDb(dbName);
runApp(const MyApp());
}
+176 -51
View File
@@ -1,48 +1,151 @@
import 'package:sqflite/sqflite.dart';
// import './state_helper.dart';
// import './db_helper.dart';
void initDB(dbName) async {
var databasesPath = await getDatabasesPath();
var path = databasesPath + dbName;
var db = await openDatabase(path);
// var db = await openDatabase('/var/db/oeffimaster/transport_db.db');
// TODO: get access to 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
// - [] route
// - [] segment
// - [] station
// - [] vehicle
// - [] line
// - [] departure
// 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();
Route.updateHighestId();
}
}
class Route {
static int highestId = 0;
static const String tableName = "route";
int id;
List<Segment> segments = List.empty();
Duration duration = Duration(minutes: 0);
Route({
required this.id,
required this.segments,
required this.duration,
});
// from trip API request
factory Route.fromJson(Map<String, dynamic> json) => Route(
segments: json["LegList"]["Leg"].map((leg) => Segment.fromJson(leg)),
duration:
json["LegList"]["Leg"][-1]["Origin"]["time"] -
json["LegList"]["Leg"][0]["Origin"]["time"],
);
// TODO: how to connect this best with the database for read/write?
factory Route.fromJson(Map<String, dynamic> json) {
Route newRoute = Route(
id: highestId,
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?
void initRouteTable(Database database) async {
database.execute("CREATE TABLE IF NOT EXISTS route();");
// 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 {
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 {
final int id = 0;
final Station startPoint;
final Station endPoint;
final DateTime startTime;
@@ -57,15 +160,21 @@ class Segment {
required this.vehicle,
});
factory Segment.fromJson(Map<String, dynamic> json) => Segment(
startPoint: Station.fromJson(json["Origin"]),
endPoint: Station.fromJson(json["Destination"]),
startTime: json["Origin"]["rtTime"],
endTime: json["Destination"]["rtTime"],
vehicle: Vehicle.fromJson(json["Product"]),
);
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.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("""
CREATE TABLE IF NOT EXISTS segment(
startpoint TEXT,
@@ -74,26 +183,38 @@ class Segment {
endtime TEXT,
vehicle INTEGER,
FOREIGN KEY(startpoint) REFERENCES station(stationid),
FOREIGN KEY(endpoint) REFERENCES station(stationid)
FOREIGN KEY(endpoint) REFERENCES station(stationid),
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 {
final String stationId;
final String id;
final String name;
List<Line>? transportLines; // could be normalized away
Station({
required this.stationId,
required this.id,
required this.name,
required this.transportLines, //TODO: make extra db table
});
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"],
transportLines: json.containsKey("productAtStop")
? json["productAtStop"].map(
@@ -102,9 +223,10 @@ class Station {
: const [],
);
void initStationTable(Database database) async {
static void initStationTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS station(
stationid TEXT PRIMARY KEY,
id TEXT PRIMARY KEY,
name TEXT
);
""");
@@ -112,24 +234,25 @@ class Station {
}
class Vehicle {
final String vehicleId;
final String id;
final Line line;
Vehicle({
required this.vehicleId,
required this.id,
required this.line,
});
// 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(
vehicleId: json["matchId"],
id: json["matchId"],
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(
vehicleid TEXT PRIMARY KEY,
id TEXT PRIMARY KEY,
line TEXT,
FOREIGN KEY(line) REFERENCES line(lineid)
);
@@ -138,7 +261,7 @@ class Vehicle {
}
class Line {
final String lineId;
final String id;
Station? destination;
Station? direction;
final VehicleType mode;
@@ -147,7 +270,7 @@ class Line {
Color? bgColor;
Line({
required this.lineId,
required this.id,
required this.name,
required this.mode,
this.fgColor,
@@ -163,7 +286,7 @@ class Line {
];
factory Line.fromJson(Map<String, dynamic> json) => Line(
lineId: json["lineId"],
id: json["lineId"],
name: json["name"],
mode: _vehicleTypeAsCatCode[int.parse(json["catCode"])],
fgColor:
@@ -173,7 +296,8 @@ class Line {
Color.fromJson(json["backgroundColor"]) as Color? ??
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(
lineid TEXT PRIMARY KEY,
destination TEXT,
@@ -199,14 +323,15 @@ class Departure {
required this.departureTime,
});
void initDepartureTable(Database database) async {
static void initDepartureTable() async {
Database database = DbHelper.db;
database.execute("""CREATE TABLE IF NOT EXISTS departure(
departureid INTEGER,
id INTEGER,
vehicle TEXT,
station TEXT,
departuretime TEXT
FOREIGN KEY(vehicle) REFERENCES vehicle(vehicleid),
FOREIGN KEY(station) REFERENCES station(stationid)
departuretime TEXT,
FOREIGN KEY(vehicle) REFERENCES vehicle(id),
FOREIGN KEY(station) REFERENCES station(id)
);
""");
}