NovySoft/novyNaplo

View on GitHub
lib/data/database/homework.dart

Summary

Maintainability
Test Coverage
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:novynaplo/data/models/homework.dart';
import 'package:novynaplo/data/models/extensions.dart';
import 'package:novynaplo/data/models/student.dart';
import 'package:novynaplo/global.dart' as globals;
import 'package:novynaplo/helpers/misc/capitalize.dart';
import 'package:novynaplo/helpers/notification/models.dart';
import 'package:novynaplo/helpers/notification/notificationDispatcher.dart';
import 'package:novynaplo/i18n/translationProvider.dart';
import 'package:sqflite/sqflite.dart';
import 'deleteSql.dart';
import 'package:flutter/foundation.dart';

Future<int> getHomeworkUser(String uid) async {
  List<Map<String, dynamic>> maps = await globals.db.rawQuery(
    'SELECT userId FROM Homework WHERE uid = ?',
    [uid],
  );
  if (maps.length == 0) {
    throw "Homework with $uid uid not found";
  }
  return maps[0]['userId'];
}

Future<List<Homework>> getAllHomework({
  bool ignoreDue = true,
  bool userSpecific = false,
}) async {
  FirebaseCrashlytics.instance.log("getAllHw");

  List<Map<String, dynamic>> maps;
  if (userSpecific) {
    maps = await globals.db.rawQuery(
      'SELECT * FROM Homework WHERE userId = ? GROUP BY userId, uid ORDER BY databaseId',
      [globals.currentUser.userId],
    );
  } else {
    maps = await globals.db.rawQuery(
      'SELECT * FROM Homework GROUP BY userId, uid ORDER BY databaseId',
    );
  }

  List<Homework> tempList = List.generate(maps.length, (i) {
    Homework temp = new Homework.fromSqlite(maps[i]);
    return temp;
  });

  //If we don't ignore the dueDate
  if (ignoreDue == false) {
    tempList.removeWhere((item) {
      //Hide if it isn't needed in the UI
      DateTime afterDue = item.dueDate;
      if (globals.howLongKeepDataForHw != -1) {
        afterDue =
            afterDue.add(Duration(days: globals.howLongKeepDataForHw.toInt()));
        if (afterDue.compareTo(DateTime.now()) < 0) {
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    });
  }
  tempList.sort((a, b) => b.dueDate.compareTo(a.dueDate));
  deleteOldHomework(tempList);
  return tempList;
}

void deleteOldHomework(List<Homework> input) async {
  Batch batch = globals.db.batch();
  bool deleted = false;
  for (var item in input) {
    DateTime afterDue = item.dueDate;
    if (item.databaseId != null) {
      afterDue = afterDue.add(Duration(days: 30));
      if (afterDue.compareTo(DateTime.now()) < 0) {
        deleted = true;
        batch.delete(
          "Homework",
          where: "databaseId = ?",
          whereArgs: [item.databaseId],
        );
      }
    }
  }
  if (deleted) {
    batch.commit();
  }
}

Future<void> batchInsertHomework(
  List<Homework> hwList,
  Student userDetails,
) async {
  FirebaseCrashlytics.instance.log("batchInsertHomework");
  bool inserted = false;
  final Batch batch = globals.db.batch();

  //We need everything that is in the db, so ignore due removing
  List<Homework> allHw = await getAllHomework(ignoreDue: true);

  for (var hw in hwList) {
    DateTime afterDue = hw.dueDate;
    afterDue = afterDue.add(Duration(days: 30));
    if (!afterDue.isBefore(DateTime.now())) {
      var matchedHw = allHw.where((element) {
        return (element.uid == hw.uid && element.userId == hw.userId);
      });

      if (matchedHw.length == 0) {
        inserted = true;
        batch.insert(
          'Homework',
          hw.toMap(),
          conflictAlgorithm: ConflictAlgorithm.replace,
        );
        if (userDetails.fetched)
          NotificationDispatcher.toBeDispatchedNotifications.homeworks.add(
            NotificationData(
              title:
                  '${(globals.allUsers.length == 1 ? getTranslatedString("newHw") : getTranslatedString(
                          "XsNewHw",
                          replaceVariables: [
                            userDetails.nickname ?? userDetails.name
                          ],
                        ))}: ' +
                      capitalize(hw.subject.name),
              subtitle:
                  "${getTranslatedString("due")}: ${hw.dueDate.toDayOnlyString()}",
              userId: hw.userId,
              uid: hw.uid,
              payload: "hw ${hw.userId} ${hw.uid}",
              additionalKey: hw.subject.name,
              isEdited: false,
            ),
          );
      } else {
        for (var n in matchedHw) {
          //!Update didn't work so we delete and create a new one
          if (n.content != hw.content ||
              n.dueDate.toUtc().toIso8601String() !=
                  hw.dueDate.toUtc().toIso8601String() ||
              n.giveUpDate.toUtc().toIso8601String() !=
                  hw.giveUpDate.toUtc().toIso8601String()) {
            inserted = true;
            batch.delete(
              "Homework",
              where: "databaseId = ?",
              whereArgs: [n.databaseId],
            );
            batch.insert(
              'Homework',
              hw.toMap(),
              conflictAlgorithm: ConflictAlgorithm.replace,
            );
            if (userDetails.fetched)
              NotificationDispatcher.toBeDispatchedNotifications.homeworks.add(
                NotificationData(
                  title:
                      '${(globals.allUsers.length == 1 ? getTranslatedString("hwModified") : getTranslatedString(
                              "XsHwModified",
                              replaceVariables: [
                                userDetails.nickname ?? userDetails.name
                              ],
                            ))}: ' +
                          capitalize(hw.subject.name),
                  subtitle:
                      "${getTranslatedString("due")}: ${hw.dueDate.toDayOnlyString()}",
                  userId: hw.userId,
                  uid: hw.uid,
                  payload: "hw ${hw.userId} ${hw.uid}",
                  additionalKey: hw.subject.name,
                  isEdited: true,
                ),
              );
          }
        }
      }
    } else {
      print("HW OUT OF DATE ${hw.dueDate}");
    }
  }
  if (inserted) {
    await batch.commit();
  }
  handleHomeworkDeletion(
    remoteHomework: hwList,
    localHomework: allHw,
    userDetails: userDetails,
  );
}

Future<void> insertHomework(
  Homework hw,
  Student userDetails, {
  bool edited = false,
}) async {
  FirebaseCrashlytics.instance.log("insertSingleHw");

  //We need everything that is in the db, so ignore due removing
  List<Homework> allHw = await getAllHomework(ignoreDue: true);
  var matchedHw = allHw.where((element) {
    return (element.uid == hw.uid && element.userId == hw.userId);
  });

  DateTime afterDue = hw.dueDate;
  afterDue = afterDue.add(Duration(days: 30));

  if (matchedHw.length == 0) {
    if (afterDue.isBefore(DateTime.now())) {
      if (hw.databaseId != null) {
        await deleteFromDbByID(hw.databaseId, "Homework");
      }
      return;
    }
    await globals.db.insert(
      'Homework',
      hw.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
    if (edited) {
      if (userDetails.fetched)
        NotificationDispatcher.toBeDispatchedNotifications.homeworks.add(
          NotificationData(
            title:
                '${(globals.allUsers.length == 1 ? getTranslatedString("hwModified") : getTranslatedString(
                        "XsHwModified",
                        replaceVariables: [
                          userDetails.nickname ?? userDetails.name
                        ],
                      ))}: ' +
                    capitalize(hw.subject.name),
            subtitle:
                "${getTranslatedString("due")}: ${hw.dueDate.toDayOnlyString()}",
            userId: hw.userId,
            uid: hw.uid,
            payload: "hw ${hw.userId} ${hw.uid}",
            additionalKey: hw.subject.name,
            isEdited: true,
          ),
        );
    } else {
      if (userDetails.fetched)
        NotificationDispatcher.toBeDispatchedNotifications.homeworks.add(
          NotificationData(
            title:
                '${(globals.allUsers.length == 1 ? getTranslatedString("newHw") : getTranslatedString(
                        "XsNewHw",
                        replaceVariables: [
                          userDetails.nickname ?? userDetails.name
                        ],
                      ))}: ' +
                    capitalize(hw.subject.name),
            subtitle:
                "${getTranslatedString("due")}: ${hw.dueDate.toDayOnlyString()}",
            userId: hw.userId,
            uid: hw.uid,
            payload: "hw ${hw.userId} ${hw.uid}",
            additionalKey: hw.subject.name,
            isEdited: false,
          ),
        );
    }
  } else {
    for (var n in matchedHw) {
      if (afterDue.compareTo(DateTime.now()) < 0) {
        if (n.databaseId != null) {
          await deleteFromDbByID(n.databaseId, "Homework");
        }
        return;
      }
      //!Update didn't work so we delete and create a new one
      if (n.content != hw.content ||
          n.dueDate.toUtc().toIso8601String() !=
              hw.dueDate.toUtc().toIso8601String() ||
          n.giveUpDate.toUtc().toIso8601String() !=
              hw.giveUpDate.toUtc().toIso8601String()) {
        await deleteFromDbByID(n.databaseId, "Homework");
        insertHomework(hw, userDetails, edited: true);
      }
    }
  }
}

Future<void> handleHomeworkDeletion({
  @required List<Homework> remoteHomework,
  @required List<Homework> localHomework,
  @required Student userDetails,
}) async {
  if (remoteHomework == null) return;
  List<Homework> filteredLocalHomeworks = List.from(localHomework)
      .where((element) => element.userId == userDetails.userId)
      .toList()
      .cast<Homework>();
  // Get a reference to the database.
  final Batch batch = globals.db.batch();
  bool deleted = false;
  for (var local in filteredLocalHomeworks) {
    if (remoteHomework.indexWhere(
          (element) =>
              element.uid == local.uid && element.userId == local.userId,
        ) ==
        -1) {
      deleted = true;
      print(
        "Local homework doesn't exist in remote $local ${local.databaseId}",
      );
      FirebaseAnalytics.instance.logEvent(
        name: "RemoteDeletion",
        parameters: {
          "dataType": "homeworks",
        },
      );
      batch.delete(
        "Homework",
        where: "databaseId = ?",
        whereArgs: [local.databaseId],
      );
    }
  }
  if (deleted) {
    await batch.commit();
  }
}