From 6c9ec194b873cbba7a36eb1818f919398e3883c3 Mon Sep 17 00:00:00 2001
From: Maximilian Betz <Maximilian.Betz@awi.de>
Date: Wed, 30 Mar 2022 11:18:31 +0200
Subject: [PATCH] more sql integration

---
 lib/addevent.dart          | 20 ++---------
 lib/databaseconnector.dart | 64 ++++++++++++++---------------------
 lib/datamodel.dart         | 47 +++++++++++++-------------
 lib/main.dart              | 12 ++++---
 lib/viewevents.dart        | 68 ++++++++++++++++++++++++++------------
 5 files changed, 104 insertions(+), 107 deletions(-)

diff --git a/lib/addevent.dart b/lib/addevent.dart
index 0f45a65..aa4086a 100644
--- a/lib/addevent.dart
+++ b/lib/addevent.dart
@@ -23,6 +23,7 @@ class _AddEventPageState extends State<AddEvent>  {
   late double accuracy = 0.0;
   late StreamSubscription<Position> streamHandler;  //For canceling GNSS stream on dispose
   final prefs = SharedPreferences.getInstance();  // Is async
+  var database = DatabaseInstance();
 
   Future startGNSS() async {
     debugPrint("Check Location Permission");
@@ -162,25 +163,8 @@ class _AddEventPageState extends State<AddEvent>  {
     final EventStoreInstance eventsStore = EventStoreInstance();
     final ConfigurationStoreInstance configuration = ConfigurationStoreInstance();
     eventsStore.currentEvent.typeId = configuration.getEventIdFromName(eventsStore.currentEvent.type);
-
     eventsStore.currentEvent.status = "PENDING";
-    eventsStore.events.add(
-        Event(
-            id: 0,
-            urnId: eventsStore.currentEvent.urnId,
-            urn: eventsStore.currentEvent.urn,
-            label: eventsStore.currentEvent.label,
-            type: eventsStore.currentEvent.type,
-            typeId: eventsStore.currentEvent.typeId,
-            description: eventsStore.currentEvent.description,
-            status: eventsStore.currentEvent.status,
-            startDate: eventsStore.currentEvent.startDate,
-            endDate: eventsStore.currentEvent.endDate,
-            latitude: eventsStore.currentEvent.latitude,
-            longitude: eventsStore.currentEvent.longitude,
-            elevation: eventsStore.currentEvent.elevation
-        ));
-
+    database.addEvent(eventsStore.currentEvent);  //TODO: display feedback after this async function
   }
 
   @override
diff --git a/lib/databaseconnector.dart b/lib/databaseconnector.dart
index da58489..f9b6b1a 100644
--- a/lib/databaseconnector.dart
+++ b/lib/databaseconnector.dart
@@ -1,12 +1,24 @@
-import 'package:flutter/material.dart';
 import 'package:sqflite/sqflite.dart';
 import 'package:path/path.dart';
 import 'dart:async';
 import 'datamodel.dart';
 
 
+class DatabaseInstance extends DatabaseConnector {
+  static final DatabaseInstance _instance = DatabaseInstance
+      ._internal();
 
-class DatabaseConnector{
+  factory DatabaseInstance() {
+    return _instance;
+  }
+
+  DatabaseInstance._internal() {
+    //connect();
+  }
+}
+
+
+abstract class DatabaseConnector{
   static const String eventDatabase = 'mobileEventLog.db';
   static const String eventTable = 'events';
   dynamic database;
@@ -25,12 +37,11 @@ class DatabaseConnector{
       },
       // Set the version. This executes the onCreate function and provides a
       // path to perform database upgrades and downgrades.
-      version: 3,
+      version: 1,
     );
   }
 
-
-  Future<int> insertNewEvent(Event event) async {
+  Future<int> addEvent(Event event) async {
       int rowId = 0;
       rowId = await database.insert(
         eventTable,
@@ -52,10 +63,7 @@ class DatabaseConnector{
   }
 
   Future<List<Event>> getEvents() async {
-    // Get a reference to the database.
-    final db = await database;
-
-    final List<Map<String, dynamic>> maps = await db.query(eventTable);
+    final List<Map<String, dynamic>> maps = await database.query(eventTable);
 
     // Convert the List<Map<String, dynamic> into a List<Event>.
     return List.generate(maps.length, (i) {
@@ -77,13 +85,14 @@ class DatabaseConnector{
     });
   }
 
+  Future<int> getPendingEventCnt() async {
+    List<Event> events =  await getPendingEvents();
+    return events.length;
+  }
 
   // A method that retrieves all the dogs from the dogs table.
   Future<List<Event>> getPendingEvents() async {
-    // Get a reference to the database.
-    final db = await database;
-
-    final List<Map<String, dynamic>> maps = await db.query(eventTable, where: '"status" = "PENDING"');
+    final List<Map<String, dynamic>> maps = await database.query(eventTable, where: 'status = ?', whereArgs: ['PENDING']);
 
     // Convert the List<Map<String, dynamic> into a List<Event>.
     return List.generate(maps.length, (i) {
@@ -98,33 +107,10 @@ class DatabaseConnector{
         status: maps[i]['status'],
         startDate: maps[i]['startDate'],
         endDate: maps[i]['endDate'],
-        latitude: maps[i]['latitude'],
-        longitude: maps[i]['longitude'],
-        elevation: maps[i]['elevation'],
+        latitude: maps[i]['latitude'].toString(),  //TODO: change datamodel to double
+        longitude: maps[i]['longitude'].toString(),
+        elevation: maps[i]['elevation'].toString(),
       );
     });
   }
-
 }
-
-
-
-
-
-
-/*
-*   int id;  // Device URN id
-  String urn;
-  String label;
-  String type; // Event type name  TODO: this should be an EventType variable
-  int typeId;
-  String description;
-  String status;
-  String startDate;
-  String endDate;
-  String latitude;
-  String longitude;
-  String elevation;
-*
-*
-* */
\ No newline at end of file
diff --git a/lib/datamodel.dart b/lib/datamodel.dart
index 53b7d62..ad65c30 100644
--- a/lib/datamodel.dart
+++ b/lib/datamodel.dart
@@ -311,7 +311,7 @@ class ConfigurationStoreInstance extends ConfigurationStoreBase {
 
 
 abstract class EventStoreBase{
-  List<Event> events = [];
+  //List<Event> events = [];
   Event currentEvent = Event(
       id: 0,
       urnId:-1,
@@ -328,7 +328,8 @@ abstract class EventStoreBase{
       elevation: ''
   );
 
-  int getPendingEventCount(){
+  /*
+  int getPendingEventCount(List<Event> events){
     int count = 0;
     for (var event in events) {
       if (event.status == 'PENDING') {
@@ -337,25 +338,25 @@ abstract class EventStoreBase{
     }
     return count;
   }
-
-  String getEventDump(){
-    String ev = '[';
-
-    for (var event in events) {
-      ev += jsonEncode(event);
-      ev += ',';
-    }
-    ev += ']';
-    return ev;
-  }
-
-  fromEventDump(dump){
-    events = [];
-    for (var entry in dump) {
-      debugPrint('Added Event from Storage: ' + entry.toString());
-      events.add(Event.fromJson(entry));
-    }
-  }
+  */
+  //String getEventDump(List<Event> events){
+  //  String ev = '[';
+
+  //  for (var event in events) {
+  //    ev += jsonEncode(event);
+  //    ev += ',';
+  //  }
+  //  ev += ']';
+  //  return ev;
+  //}
+
+  //fromEventDump(dump){
+  //  events = [];
+  //  for (var entry in dump) {
+  //    debugPrint('Added Event from Storage: ' + entry.toString());
+  //    events.add(Event.fromJson(entry));
+  //  }
+  //}
 }
 
 
@@ -367,7 +368,7 @@ class EventStoreInstance extends EventStoreBase {
   }
 
   EventStoreInstance._internal() {
-    events = [];
+    //events = [];
     currentEvent = Event(
         id:0,
         urnId:-1,
@@ -386,7 +387,7 @@ class EventStoreInstance extends EventStoreBase {
   }
 
   void reset(){
-    events = [];
+    //events = [];
     currentEvent = Event(
         id:0,
         urnId:-1,
diff --git a/lib/main.dart b/lib/main.dart
index 7fa259d..68ab8a3 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -34,8 +34,8 @@ Future<void> pathStuff() async {
   //Map<String, String> allValues = await storage.readAll();
   //print('Secure Storage: ' + allValues.toString());
 
-  var database = DatabaseConnector();
-  await database.connect();
+  var database = DatabaseInstance();
+  await database.connect();  //Do once at start of APP!
 
   Event event = Event.fromJson({"id":0,"urnId":102,"urn":"mooring:f9-12","label":"hggg","type":"Calibration",
     "typeId":15,"description":"ggggg","status":"PENDING",
@@ -47,12 +47,12 @@ Future<void> pathStuff() async {
 
   //Update second event in sql database
   event.id = 2;
-  event.description = 'updated description';
-  database.updateEvent(event);
+  event.description = 'updated description1';
+  //database.updateEvent(event);
 
 
   //debugPrint("Add Event to database: " + event.toString());
-  //int rowId = await database.insertEvent(event);
+  //int rowId = await database.addEvent(event);
   //debugPrint("Events row Id: " + rowId.toString());
 
   tempEvents = await database.getEvents();
@@ -86,6 +86,7 @@ void main() {
   //configuration.currentCollection = Collection.fromJson({"id":1,"description":"","collectionName":"FRAM"});
   //configuration.initialized = true;
 
+  /*
 events.fromEventDump(
   [
     {"id":1,"urnId":102,"urn":"mooring:f9-12","label":"hggg","type":"Calibration",
@@ -101,6 +102,7 @@ events.fromEventDump(
   ]);
   //events.fromEventDump([{"id":102,"urn":"mooring:f9-12","label":"cf","type":"Calibration","typeId":15,"description":"fd","status":"PENDING","startDate":"2022-03-25T12:47:30.659436Z","endDate":"2022-03-25T12:47:30.659436Z","latitude":"","longitude":"","elevation":""},{"id":102,"urn":"mooring:f9-12","label":"cf","type":"Calibration","typeId":15,"description":"fd","status":"PENDING","startDate":"2022-03-25T12:47:32.136009Z","endDate":"2022-03-25T12:47:32.136009Z","latitude":"","longitude":"","elevation":""}]);
 
+*/
 
   runApp(MaterialApp(
     title: 'Mobile Event Log',
diff --git a/lib/viewevents.dart b/lib/viewevents.dart
index c883ee7..fde41ac 100644
--- a/lib/viewevents.dart
+++ b/lib/viewevents.dart
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
 import 'datamodel.dart';
 import 'package:http/http.dart' as http;
 import 'sensorconnector.dart';
+import 'databaseconnector.dart';
 
 class ViewEvents extends StatefulWidget {
   const ViewEvents({Key? key}) : super(key: key);
@@ -16,20 +17,43 @@ class _ViewEvents extends State<ViewEvents> {
   // Get singleton to access locally stored events:
   final EventStoreInstance _events = EventStoreInstance();
   String _syncStatus = '';
-  //TODO: add exception handling and display exceptions to user.
+
+  var database = DatabaseInstance();
+  List<Event> localEvents = [];
+
+
+  Future<void> fetchEventsFromDb() async{
+    localEvents = await database.getEvents();
+    int pendingEventCnt = await database.getPendingEventCnt();
+
+    debugPrint("Database Events: ");
+    for (var event in localEvents){
+      debugPrint(event.toString());
+    }
+
+    _syncStatus = pendingEventCnt.toString() + ' event(s) pending';
+    setState(() {});  //Got events from database, so update UI
+  }
 
   @override
   void initState() {
-    _syncStatus = _events.getPendingEventCount().toString() + ' event(s) pending';
+    _syncStatus = '';
     super.initState();
 
-    //TODO: Query all events from database everytime this widget is opened. Remove local event storage instance!
+    fetchEventsFromDb();
   }
 
   Future<void> syncEvents() async {
     final ConfigurationStoreInstance configuration = ConfigurationStoreInstance();
     SensorConnector connection = SensorConnector();
-    int syncCounter = _events.getPendingEventCount();; //For displaying progress during event upload
+
+    List<Event> events = await database.getPendingEvents();
+    debugPrint('Pending Events');
+    for (var event in events){
+      debugPrint(event.toString());
+    }
+
+    int syncCounter = events.length;
 
     if(syncCounter > 0) {
       try {
@@ -38,24 +62,24 @@ class _ViewEvents extends State<ViewEvents> {
             configuration.loginInformation.password);
 
         if (token != null) {
-          debugPrint('Number of Events: ' + _events.events.length.toString());
+          debugPrint('Number of Events: ' + events.length.toString());
           debugPrint('Number of Pending Events: ' + syncCounter.toString());
           var index = 0;
-          for (var event in _events.events) {
-            if (event.status == 'PENDING') {  //TODO: only query events which are 'PENDING' from database.
-              debugPrint('Idx: ' + index.toString() + ' ' +
-                  event.toSensorJson().toString());
-              index++;
-              if (await connection.putEvent(event, token) == true) {
-                //Event has been posted to Sensor.
-                syncCounter--;
-                debugPrint(
-                    'put success, remaining events: ' + syncCounter.toString());
-                event.status = 'EXPORTED'; //Update event export only once    //TODO: move to database and update event based in primary row id.
-                setState(() {});
-              } else {
-                throw Exception('Sync for ' + event.urn + 'failed');
-              }
+          for (var event in events) {
+            debugPrint('Idx: ' + index.toString() + ' ' +
+                event.toSensorJson().toString());
+            index++;
+            if (await connection.putEvent(event, token) == true) {
+              //Event has been posted to Sensor.
+              syncCounter--;
+              debugPrint(
+                  'put success, remaining events: ' + syncCounter.toString());
+              event.status = 'EXPORTED'; //Update event to export only once
+              database.updateEvent(event); //Update Event in SQL Database
+              fetchEventsFromDb(); //update view list   //TODO: this is bad performance
+              setState(() {});
+            } else {
+              throw Exception('Sync for ' + event.urn + 'failed');
             }
           }
           _syncStatus = 'export success';
@@ -159,7 +183,7 @@ class _ViewEvents extends State<ViewEvents> {
                 ),
               ],
               rows: <DataRow>[
-                for (var event in _events.events)
+                for (var event in localEvents)
                   DataRow(
                     cells: <DataCell>[
                       DataCell(Text(event.urnId.toString())),
@@ -245,7 +269,7 @@ class _ViewEvents extends State<ViewEvents> {
               icon: null,
               label: const Text('Sync'),
               onPressed: () {
-                debugPrint(_events.getEventDump());
+                //debugPrint(_events.getEventDump());
 
 
                 syncEvents();
-- 
GitLab